public override void ProcessClientKeyExchange(Stream input)
 {
     byte[] array = TlsUtilities.ReadOpaque16(input);
     mPsk = mPskIdentityManager.GetPsk(array);
     if (mPsk == null)
     {
         throw new TlsFatalAlert(115);
     }
     mContext.SecurityParameters.pskIdentity = array;
     if (mKeyExchange == 14)
     {
         BigInteger y = TlsDHUtilities.ReadDHParameter(input);
         mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(y, mDHParameters));
     }
     else if (mKeyExchange == 24)
     {
         byte[]             encoding   = TlsUtilities.ReadOpaque8(input);
         ECDomainParameters parameters = mECAgreePrivateKey.Parameters;
         mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(mServerECPointFormats, parameters, encoding));
     }
     else if (mKeyExchange == 15)
     {
         byte[] encryptedPreMasterSecret = (!TlsUtilities.IsSsl(mContext)) ? TlsUtilities.ReadOpaque16(input) : Streams.ReadAll(input);
         mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
     }
 }
        protected override void HandleAlertWarningMessage(byte alertDescription)
        {
            base.HandleAlertWarningMessage(alertDescription);

            switch (alertDescription)
            {
            case AlertDescription.no_certificate:
            {
                /*
                 * SSL 3.0 If the server has sent a certificate request Message, the client must send
                 * either the certificate message or a no_certificate alert.
                 */
                if (TlsUtilities.IsSsl(Context) && this.mCertificateRequest != null)
                {
                    switch (this.mConnectionState)
                    {
                    case CS_SERVER_HELLO_DONE:
                    case CS_CLIENT_SUPPLEMENTAL_DATA:
                    {
                        if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
                        {
                            mTlsServer.ProcessClientSupplementalData(null);
                        }

                        NotifyClientCertificate(Certificate.EmptyChain);
                        this.mConnectionState = CS_CLIENT_CERTIFICATE;
                        return;
                    }
                    }
                }
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }
            }
        }
Exemple #3
0
        public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length,
                                                       int fullLength, byte[] dummyData)
        {
            /*
             * Actual MAC only calculated on 'length' bytes...
             */
            byte[] result = CalculateMac(seqNo, type, message, offset, length);

            /*
             * ...but ensure a constant number of complete digest blocks are processed (as many as would
             * be needed for 'fullLength' bytes of input).
             */
            int headerLength = TlsUtilities.IsSsl(context) ? 11 : 13;

            // How many extra full blocks do we need to calculate?
            int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + length);

            while (--extra >= 0)
            {
                mac.BlockUpdate(dummyData, 0, digestBlockSize);
            }

            // One more byte in case the implementation is "lazy" about processing blocks
            mac.Update(dummyData[0]);
            mac.Reset();

            return(result);
        }
    protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
    {
        int  num  = off + len;
        byte b    = buf[num - 1];
        int  num2 = b & 0xFF;
        int  num3 = num2 + 1;
        int  num4 = 0;
        byte b2   = 0;

        if ((TlsUtilities.IsSsl(context) && num3 > blockSize) || macSize + num3 > len)
        {
            num3 = 0;
        }
        else
        {
            int num5 = num - num3;
            do
            {
                b2 = (byte)(b2 | (byte)(buf[num5++] ^ b));
            }while (num5 < num);
            num4 = num3;
            if (b2 != 0)
            {
                num3 = 0;
            }
        }
        byte[] array = randomData;
        while (num4 < 256)
        {
            b2 = (byte)(b2 | (byte)(array[num4++] ^ b));
        }
        byte[] array2;
        (array2 = array)[0] = (byte)(array2[0] ^ b2);
        return(num3);
    }
    public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen)
    {
        this.context = context;
        KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);

        secret = Arrays.Clone(keyParameter.GetKey());
        if (digest is LongDigest)
        {
            digestBlockSize = 128;
            digestOverhead  = 16;
        }
        else
        {
            digestBlockSize = 64;
            digestOverhead  = 8;
        }
        if (TlsUtilities.IsSsl(context))
        {
            mac = new Ssl3Mac(digest);
            if (digest.GetDigestSize() == 20)
            {
                digestOverhead = 4;
            }
        }
        else
        {
            mac = new HMac(digest);
        }
        mac.Init(keyParameter);
        macLength = mac.GetMacSize();
        if (context.SecurityParameters.truncatedHMac)
        {
            macLength = Math.Min(macLength, 10);
        }
    }
Exemple #6
0
 protected virtual void RefuseRenegotiation()
 {
     if (TlsUtilities.IsSsl(Context))
     {
         throw new TlsFatalAlert(40);
     }
     RaiseWarning(100, "Renegotiation not supported");
 }
Exemple #7
0
    protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
    {
        IDigest digest = handshakeHash.ForkPrfHash();

        if (sslSender != null && TlsUtilities.IsSsl(context))
        {
            digest.BlockUpdate(sslSender, 0, sslSender.Length);
        }
        return(DigestUtilities.DoFinal(digest));
    }
Exemple #8
0
    public virtual int DoFinal(byte[] output, int outOff)
    {
        if (mContext != null && TlsUtilities.IsSsl(mContext))
        {
            Ssl3Complete(mMd5, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 48);
            Ssl3Complete(mSha1, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 40);
        }
        int num  = mMd5.DoFinal(output, outOff);
        int num2 = mSha1.DoFinal(output, outOff + num);

        return(num + num2);
    }
    public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length, int fullLength, byte[] dummyData)
    {
        byte[] result = CalculateMac(seqNo, type, message, offset, length);
        int    num    = TlsUtilities.IsSsl(context) ? 11 : 13;
        int    num2   = GetDigestBlockCount(num + fullLength) - GetDigestBlockCount(num + length);

        while (--num2 >= 0)
        {
            mac.BlockUpdate(dummyData, 0, digestBlockSize);
        }
        mac.Update(dummyData[0]);
        mac.Reset();
        return(result);
    }
Exemple #10
0
        /**
         * Generate a new instance of an TlsMac.
         *
         * @param context the TLS client context
         * @param digest  The digest to use.
         * @param key     A byte-array where the key for this MAC is located.
         * @param keyOff  The number of bytes to skip, before the key starts in the buffer.
         * @param keyLen  The length of the key.
         */
        public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen)
        {
            this.context = context;

            KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);

            this.secret = Arrays.Clone(keyParameter.GetKey());

            // TODO This should check the actual algorithm, not rely on the engine type
            if (digest is LongDigest)
            {
                this.digestBlockSize = 128;
                this.digestOverhead  = 16;
            }
            else
            {
                this.digestBlockSize = 64;
                this.digestOverhead  = 8;
            }

            if (TlsUtilities.IsSsl(context))
            {
                this.mac = new Ssl3Mac(digest);

                // TODO This should check the actual algorithm, not assume based on the digest size
                if (digest.GetDigestSize() == 20)
                {
                    /*
                     * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not
                     * digest block-aligned.
                     */
                    this.digestOverhead = 4;
                }
            }
            else
            {
                this.mac = new HMac(digest);

                // NOTE: The input pad for HMAC is always a full digest block
            }

            this.mac.Init(keyParameter);

            this.macLength = mac.GetMacSize();
            if (context.SecurityParameters.truncatedHMac)
            {
                this.macLength = System.Math.Min(this.macLength, 10);
            }
        }
        public override void ProcessClientKeyExchange(Stream input)
        {
            byte[] encryptedPreMasterSecret;
            if (TlsUtilities.IsSsl(mContext))
            {
                // TODO Do any SSLv3 clients actually include the length?
                encryptedPreMasterSecret = Streams.ReadAll(input);
            }
            else
            {
                encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
            }

            this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
        }
    protected override void HandleWarningMessage(byte description)
    {
        byte b = description;

        if (b == 41)
        {
            if (TlsUtilities.IsSsl(Context) && mCertificateRequest != null)
            {
                NotifyClientCertificate(Certificate.EmptyChain);
            }
        }
        else
        {
            base.HandleWarningMessage(description);
        }
    }
        public override void ProcessClientKeyExchange(Stream input)
        {
            byte[] psk_identity = TlsUtilities.ReadOpaque16(input);

            this.mPsk = mPskIdentityManager.GetPsk(psk_identity);
            if (mPsk == null)
            {
                throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
            }

            mContext.SecurityParameters.pskIdentity = psk_identity;

            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
            {
                BigInteger Yc = TlsDHUtilities.ReadDHParameter(input);

                this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters));
            }
            else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
            {
                byte[] point = TlsUtilities.ReadOpaque8(input);

                ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters;

                this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
                                                                                 mServerECPointFormats, curve_params, point));
            }
            else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
            {
                byte[] encryptedPreMasterSecret;
                if (TlsUtilities.IsSsl(mContext))
                {
                    // TODO Do any SSLv3 clients actually include the length?
                    encryptedPreMasterSecret = Streams.ReadAll(input);
                }
                else
                {
                    encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
                }

                this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
            }
        }
Exemple #14
0
        protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
        {
            int  end      = off + len;
            byte lastByte = buf[end - 1];
            int  padlen   = lastByte & 0xff;
            int  totalPad = padlen + 1;

            int  dummyIndex = 0;
            byte padDiff    = 0;

            if ((TlsUtilities.IsSsl(context) && totalPad > blockSize) || (macSize + totalPad > len))
            {
                totalPad = 0;
            }
            else
            {
                int padPos = end - totalPad;
                do
                {
                    padDiff |= (byte)(buf[padPos++] ^ lastByte);
                }while (padPos < end);

                dummyIndex = totalPad;

                if (padDiff != 0)
                {
                    totalPad = 0;
                }
            }

            // Run some extra dummy checks so the number of checks is always constant
            {
                byte[] dummyPad = randomData;
                while (dummyIndex < 256)
                {
                    padDiff |= (byte)(dummyPad[dummyIndex++] ^ lastByte);
                }
                // Ensure the above loop is not eliminated
                dummyPad[0] ^= padDiff;
            }

            return(totalPad);
        }
        protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf)
        {
            mKeyExchange.ProcessClientKeyExchange(buf);

            AssertEmpty(buf);

            if (TlsUtilities.IsSsl(Context))
            {
                EstablishMasterSecret(Context, mKeyExchange);
            }

            this.mPrepareFinishHash = mRecordStream.PrepareToFinish();
            this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, mPrepareFinishHash, null);

            if (!TlsUtilities.IsSsl(Context))
            {
                EstablishMasterSecret(Context, mKeyExchange);
            }

            mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
        }
Exemple #16
0
        /// <exception cref="IOException"></exception>
        public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey,
                                                              Stream output)
        {
            /*
             * Choose a PremasterSecret and send it encrypted to the server
             */
            byte[] premasterSecret = new byte[48];
            context.SecureRandom.NextBytes(premasterSecret);
            TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0);

            Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());

            encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom));

            try
            {
                byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);

                if (TlsUtilities.IsSsl(context))
                {
                    // TODO Do any SSLv3 servers actually expect the length?
                    output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length);
                }
                else
                {
                    TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output);
                }
            }
            catch (InvalidCipherTextException e)
            {
                /*
                 * This should never happen, only during decryption.
                 */
                throw new TlsFatalAlert(AlertDescription.internal_error, e);
            }

            return(premasterSecret);
        }
Exemple #17
0
        protected override void HandleWarningMessage(byte description)
        {
            switch (description)
            {
            case AlertDescription.no_certificate:
            {
                /*
                 * SSL 3.0 If the server has sent a certificate request Message, the client must Send
                 * either the certificate message or a no_certificate alert.
                 */
                if (TlsUtilities.IsSsl(Context) && mCertificateRequest != null)
                {
                    NotifyClientCertificate(Certificate.EmptyChain);
                }
                break;
            }

            default:
            {
                base.HandleWarningMessage(description);
                break;
            }
            }
        }
        protected override void HandleHandshakeMessage(byte type, MemoryStream buf)
        {
            switch (type)
            {
            case HandshakeType.client_hello:
            {
                switch (this.mConnectionState)
                {
                case CS_START:
                {
                    ReceiveClientHelloMessage(buf);
                    this.mConnectionState = CS_CLIENT_HELLO;

                    SendServerHelloMessage();
                    this.mConnectionState = CS_SERVER_HELLO;

                    mRecordStream.NotifyHelloComplete();

                    IList serverSupplementalData = mTlsServer.GetServerSupplementalData();
                    if (serverSupplementalData != null)
                    {
                        SendSupplementalDataMessage(serverSupplementalData);
                    }
                    this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;

                    this.mKeyExchange = mTlsServer.GetKeyExchange();
                    this.mKeyExchange.Init(Context);

                    this.mServerCredentials = mTlsServer.GetCredentials();

                    Certificate serverCertificate = null;

                    if (this.mServerCredentials == null)
                    {
                        this.mKeyExchange.SkipServerCredentials();
                    }
                    else
                    {
                        this.mKeyExchange.ProcessServerCredentials(this.mServerCredentials);

                        serverCertificate = this.mServerCredentials.Certificate;
                        SendCertificateMessage(serverCertificate);
                    }
                    this.mConnectionState = CS_SERVER_CERTIFICATE;

                    // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
                    if (serverCertificate == null || serverCertificate.IsEmpty)
                    {
                        this.mAllowCertificateStatus = false;
                    }

                    if (this.mAllowCertificateStatus)
                    {
                        CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus();
                        if (certificateStatus != null)
                        {
                            SendCertificateStatusMessage(certificateStatus);
                        }
                    }

                    this.mConnectionState = CS_CERTIFICATE_STATUS;

                    byte[] serverKeyExchange = this.mKeyExchange.GenerateServerKeyExchange();
                    if (serverKeyExchange != null)
                    {
                        SendServerKeyExchangeMessage(serverKeyExchange);
                    }
                    this.mConnectionState = CS_SERVER_KEY_EXCHANGE;

                    if (this.mServerCredentials != null)
                    {
                        this.mCertificateRequest = mTlsServer.GetCertificateRequest();
                        if (this.mCertificateRequest != null)
                        {
                            if (TlsUtilities.IsTlsV12(Context) != (mCertificateRequest.SupportedSignatureAlgorithms != null))
                            {
                                throw new TlsFatalAlert(AlertDescription.internal_error);
                            }

                            this.mKeyExchange.ValidateCertificateRequest(mCertificateRequest);

                            SendCertificateRequestMessage(mCertificateRequest);

                            TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
                                                             this.mCertificateRequest.SupportedSignatureAlgorithms);
                        }
                    }
                    this.mConnectionState = CS_CERTIFICATE_REQUEST;

                    SendServerHelloDoneMessage();
                    this.mConnectionState = CS_SERVER_HELLO_DONE;

                    this.mRecordStream.HandshakeHash.SealHashAlgorithms();

                    break;
                }

                case CS_END:
                {
                    RefuseRenegotiation();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.supplemental_data:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                {
                    mTlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf));
                    this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.certificate:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                case CS_CLIENT_SUPPLEMENTAL_DATA:
                {
                    if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
                    {
                        mTlsServer.ProcessClientSupplementalData(null);
                    }

                    if (this.mCertificateRequest == null)
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ReceiveCertificateMessage(buf);
                    this.mConnectionState = CS_CLIENT_CERTIFICATE;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.client_key_exchange:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO_DONE:
                case CS_CLIENT_SUPPLEMENTAL_DATA:
                case CS_CLIENT_CERTIFICATE:
                {
                    if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA)
                    {
                        mTlsServer.ProcessClientSupplementalData(null);
                    }

                    if (mConnectionState < CS_CLIENT_CERTIFICATE)
                    {
                        if (this.mCertificateRequest == null)
                        {
                            this.mKeyExchange.SkipClientCredentials();
                        }
                        else
                        {
                            if (TlsUtilities.IsTlsV12(Context))
                            {
                                /*
                                 * RFC 5246 If no suitable certificate is available, the client MUST Send a
                                 * certificate message containing no certificates.
                                 *
                                 * NOTE: In previous RFCs, this was SHOULD instead of MUST.
                                 */
                                throw new TlsFatalAlert(AlertDescription.unexpected_message);
                            }
                            else if (TlsUtilities.IsSsl(Context))
                            {
                                if (this.mPeerCertificate == null)
                                {
                                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                                }
                            }
                            else
                            {
                                NotifyClientCertificate(Certificate.EmptyChain);
                            }
                        }
                    }

                    ReceiveClientKeyExchangeMessage(buf);
                    this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.certificate_verify:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_KEY_EXCHANGE:
                {
                    /*
                     * RFC 5246 7.4.8 This message is only sent following a client certificate that has
                     * signing capability (i.e., all certificates except those containing fixed
                     * Diffie-Hellman parameters).
                     */
                    if (!ExpectCertificateVerifyMessage())
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ReceiveCertificateVerifyMessage(buf);
                    this.mConnectionState = CS_CERTIFICATE_VERIFY;

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.finished:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_KEY_EXCHANGE:
                case CS_CERTIFICATE_VERIFY:
                {
                    if (mConnectionState < CS_CERTIFICATE_VERIFY && ExpectCertificateVerifyMessage())
                    {
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ProcessFinishedMessage(buf);
                    this.mConnectionState = CS_CLIENT_FINISHED;

                    if (this.mExpectSessionTicket)
                    {
                        SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
                    }
                    this.mConnectionState = CS_SERVER_SESSION_TICKET;

                    SendChangeCipherSpecMessage();
                    SendFinishedMessage();
                    this.mConnectionState = CS_SERVER_FINISHED;

                    CompleteHandshake();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.hello_request:
            case HandshakeType.hello_verify_request:
            case HandshakeType.server_hello:
            case HandshakeType.server_key_exchange:
            case HandshakeType.certificate_request:
            case HandshakeType.server_hello_done:
            case HandshakeType.session_ticket:
            default:
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }
        }
    protected override void HandleHandshakeMessage(byte type, byte[] data)
    {
        MemoryStream memoryStream = new MemoryStream(data);

        switch (type)
        {
        case 1:
            switch (base.mConnectionState)
            {
            case 0:
            {
                ReceiveClientHelloMessage(memoryStream);
                base.mConnectionState = 1;
                SendServerHelloMessage();
                base.mConnectionState = 2;
                mRecordStream.NotifyHelloComplete();
                IList serverSupplementalData = mTlsServer.GetServerSupplementalData();
                if (serverSupplementalData != null)
                {
                    SendSupplementalDataMessage(serverSupplementalData);
                }
                base.mConnectionState = 3;
                mKeyExchange          = mTlsServer.GetKeyExchange();
                mKeyExchange.Init(Context);
                mServerCredentials = mTlsServer.GetCredentials();
                Certificate certificate = null;
                if (mServerCredentials == null)
                {
                    mKeyExchange.SkipServerCredentials();
                }
                else
                {
                    mKeyExchange.ProcessServerCredentials(mServerCredentials);
                    certificate = mServerCredentials.Certificate;
                    SendCertificateMessage(certificate);
                }
                base.mConnectionState = 4;
                if (certificate == null || certificate.IsEmpty)
                {
                    mAllowCertificateStatus = false;
                }
                if (mAllowCertificateStatus)
                {
                    CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus();
                    if (certificateStatus != null)
                    {
                        SendCertificateStatusMessage(certificateStatus);
                    }
                }
                base.mConnectionState = 5;
                byte[] array = mKeyExchange.GenerateServerKeyExchange();
                if (array != null)
                {
                    SendServerKeyExchangeMessage(array);
                }
                base.mConnectionState = 6;
                if (mServerCredentials != null)
                {
                    mCertificateRequest = mTlsServer.GetCertificateRequest();
                    if (mCertificateRequest != null)
                    {
                        if (TlsUtilities.IsTlsV12(Context) != (mCertificateRequest.SupportedSignatureAlgorithms != null))
                        {
                            throw new TlsFatalAlert(80);
                        }
                        mKeyExchange.ValidateCertificateRequest(mCertificateRequest);
                        SendCertificateRequestMessage(mCertificateRequest);
                        TlsUtilities.TrackHashAlgorithms(mRecordStream.HandshakeHash, mCertificateRequest.SupportedSignatureAlgorithms);
                    }
                }
                base.mConnectionState = 7;
                SendServerHelloDoneMessage();
                base.mConnectionState = 8;
                mRecordStream.HandshakeHash.SealHashAlgorithms();
                break;
            }

            case 16:
                RefuseRenegotiation();
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 23:
        {
            short mConnectionState = base.mConnectionState;
            if (mConnectionState == 8)
            {
                mTlsServer.ProcessClientSupplementalData(TlsProtocol.ReadSupplementalDataMessage(memoryStream));
                base.mConnectionState = 9;
                break;
            }
            throw new TlsFatalAlert(10);
        }

        case 11:
            switch (base.mConnectionState)
            {
            case 8:
            case 9:
                if (base.mConnectionState < 9)
                {
                    mTlsServer.ProcessClientSupplementalData(null);
                }
                if (mCertificateRequest == null)
                {
                    throw new TlsFatalAlert(10);
                }
                ReceiveCertificateMessage(memoryStream);
                base.mConnectionState = 10;
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 16:
            switch (base.mConnectionState)
            {
            case 8:
            case 9:
            case 10:
                if (base.mConnectionState < 9)
                {
                    mTlsServer.ProcessClientSupplementalData(null);
                }
                if (base.mConnectionState < 10)
                {
                    if (mCertificateRequest == null)
                    {
                        mKeyExchange.SkipClientCredentials();
                    }
                    else
                    {
                        if (TlsUtilities.IsTlsV12(Context))
                        {
                            throw new TlsFatalAlert(10);
                        }
                        if (TlsUtilities.IsSsl(Context))
                        {
                            if (mPeerCertificate == null)
                            {
                                throw new TlsFatalAlert(10);
                            }
                        }
                        else
                        {
                            NotifyClientCertificate(Certificate.EmptyChain);
                        }
                    }
                }
                ReceiveClientKeyExchangeMessage(memoryStream);
                base.mConnectionState = 11;
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        case 15:
        {
            short mConnectionState = base.mConnectionState;
            if (mConnectionState == 11)
            {
                if (!ExpectCertificateVerifyMessage())
                {
                    throw new TlsFatalAlert(10);
                }
                ReceiveCertificateVerifyMessage(memoryStream);
                base.mConnectionState = 12;
                break;
            }
            throw new TlsFatalAlert(10);
        }

        case 20:
            switch (base.mConnectionState)
            {
            case 11:
            case 12:
                if (base.mConnectionState < 12 && ExpectCertificateVerifyMessage())
                {
                    throw new TlsFatalAlert(10);
                }
                ProcessFinishedMessage(memoryStream);
                base.mConnectionState = 13;
                if (mExpectSessionTicket)
                {
                    SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
                    SendChangeCipherSpecMessage();
                }
                base.mConnectionState = 14;
                SendFinishedMessage();
                base.mConnectionState = 15;
                base.mConnectionState = 16;
                CompleteHandshake();
                break;

            default:
                throw new TlsFatalAlert(10);
            }
            break;

        default:
            throw new TlsFatalAlert(10);
        }
    }
        protected override void HandleHandshakeMessage(byte type, MemoryStream buf)
        {
            if (HTTPManager.Logger.Level <= Loglevels.All)
            {
                HTTPManager.Logger.Verbose("TlsClientProtocol", "HandleHandshakeMessage " + type.ToString(), this.LoggingContext);
            }

            if (this.mResumedSession)
            {
                if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO)
                {
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                ProcessFinishedMessage(buf);
                this.mConnectionState = CS_SERVER_FINISHED;

                SendChangeCipherSpecMessage();
                SendFinishedMessage();
                this.mConnectionState = CS_CLIENT_FINISHED;

                CompleteHandshake();
                return;
            }

            switch (type)
            {
            case HandshakeType.certificate:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                {
                    if (this.mConnectionState == CS_SERVER_HELLO)
                    {
                        HandleSupplementalData(null);
                    }

                    // Parse the Certificate message and Send to cipher suite

                    this.mPeerCertificate = Certificate.Parse(buf);

                    AssertEmpty(buf);

                    // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
                    if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty)
                    {
                        this.mAllowCertificateStatus = false;
                    }

                    this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate);

                    this.mAuthentication = mTlsClient.GetAuthentication();

                    // We are expecting a Certificate_Status message, so going to delay the NotifyServerCertificate call
                    // until the server_hello_done. (no until CS_SERVER_CERTIFICATE as the server might not send one)
                    // This way, we can validate both the certificate status and the received certificates.
                    if (!this.mTlsClient.ExpectEmptyCertificateStatusExtension)
                    {
                        this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
                    }

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_SERVER_CERTIFICATE;
                break;
            }

            case HandshakeType.certificate_status:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_CERTIFICATE:
                {
                    if (!this.mAllowCertificateStatus)
                    {
                        /*
                         * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
                         * server MUST have included an extension of type "status_request" with empty
                         * "extension_data" in the extended server hello..
                         */
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    this.mTlsClient.CertificateStatus = CertificateStatus.Parse(buf);

                    AssertEmpty(buf);

                    // TODO[RFC 3546] Figure out how to provide this to the client/authentication.

                    this.mConnectionState = CS_CERTIFICATE_STATUS;
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.finished:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_FINISHED:
                case CS_SERVER_SESSION_TICKET:
                {
                    if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket)
                    {
                        /*
                         * RFC 5077 3.3. This message MUST be sent if the server included a
                         * SessionTicket extension in the ServerHello.
                         */
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    ProcessFinishedMessage(buf);
                    this.mConnectionState = CS_SERVER_FINISHED;

                    CompleteHandshake();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.server_hello:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_HELLO:
                {
                    ReceiveServerHelloMessage(buf);
                    this.mConnectionState = CS_SERVER_HELLO;

                    this.mRecordStream.NotifyHelloComplete();

                    ApplyMaxFragmentLengthExtension();

                    if (this.mResumedSession)
                    {
                        this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
                        this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
                    }
                    else
                    {
                        InvalidateSession();

                        if (this.mSelectedSessionID.Length > 0)
                        {
                            this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null);
                        }
                    }

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.supplemental_data:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                {
                    HandleSupplementalData(ReadSupplementalDataMessage(buf));
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }
                break;
            }

            case HandshakeType.server_hello_done:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                case CS_SERVER_KEY_EXCHANGE:
                case CS_CERTIFICATE_REQUEST:
                {
                    if (this.mTlsClient.ExpectEmptyCertificateStatusExtension)
                    {
                        this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
                    }

                    if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
                    {
                        HandleSupplementalData(null);
                    }

                    if (mConnectionState < CS_SERVER_CERTIFICATE)
                    {
                        // There was no server certificate message; check it's OK
                        this.mKeyExchange.SkipServerCredentials();
                        this.mAuthentication = null;
                    }

                    if (mConnectionState < CS_SERVER_KEY_EXCHANGE)
                    {
                        // There was no server key exchange message; check it's OK
                        this.mKeyExchange.SkipServerKeyExchange();
                    }

                    AssertEmpty(buf);

                    this.mConnectionState = CS_SERVER_HELLO_DONE;

                    this.mRecordStream.HandshakeHash.SealHashAlgorithms();

                    IList clientSupplementalData = mTlsClient.GetClientSupplementalData();
                    if (clientSupplementalData != null)
                    {
                        SendSupplementalDataMessage(clientSupplementalData);
                    }
                    this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;

                    TlsCredentials clientCreds = null;
                    if (mCertificateRequest == null)
                    {
                        this.mKeyExchange.SkipClientCredentials();
                    }
                    else
                    {
                        clientCreds = this.mAuthentication.GetClientCredentials(Context, mCertificateRequest);

                        if (clientCreds == null)
                        {
                            this.mKeyExchange.SkipClientCredentials();

                            /*
                             * RFC 5246 If no suitable certificate is available, the client MUST Send a
                             * certificate message containing no certificates.
                             *
                             * NOTE: In previous RFCs, this was SHOULD instead of MUST.
                             */
                            SendCertificateMessage(Certificate.EmptyChain);
                        }
                        else
                        {
                            this.mKeyExchange.ProcessClientCredentials(clientCreds);

                            SendCertificateMessage(clientCreds.Certificate);
                        }
                    }

                    this.mConnectionState = CS_CLIENT_CERTIFICATE;

                    /*
                     * Send the client key exchange message, depending on the key exchange we are using
                     * in our CipherSuite.
                     */
                    SendClientKeyExchangeMessage();
                    this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;

                    if (TlsUtilities.IsSsl(Context))
                    {
                        EstablishMasterSecret(Context, mKeyExchange);
                    }

                    TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish();
                    this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null);

                    if (!TlsUtilities.IsSsl(Context))
                    {
                        EstablishMasterSecret(Context, mKeyExchange);
                    }

                    mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());

                    if (clientCreds != null && clientCreds is TlsSignerCredentials)
                    {
                        TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;

                        /*
                         * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                         */
                        SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
                            Context, signerCredentials);

                        byte[] hash;
                        if (signatureAndHashAlgorithm == null)
                        {
                            hash = mSecurityParameters.SessionHash;
                        }
                        else
                        {
                            hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
                        }

                        byte[]          signature         = signerCredentials.GenerateCertificateSignature(hash);
                        DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
                        SendCertificateVerifyMessage(certificateVerify);

                        this.mConnectionState = CS_CERTIFICATE_VERIFY;
                    }

                    SendChangeCipherSpecMessage();
                    SendFinishedMessage();
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_CLIENT_FINISHED;
                break;
            }

            case HandshakeType.server_key_exchange:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_HELLO:
                case CS_SERVER_SUPPLEMENTAL_DATA:
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                {
                    if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
                    {
                        HandleSupplementalData(null);
                    }

                    if (mConnectionState < CS_SERVER_CERTIFICATE)
                    {
                        // There was no server certificate message; check it's OK
                        this.mKeyExchange.SkipServerCredentials();
                        this.mAuthentication = null;
                    }

                    this.mKeyExchange.ProcessServerKeyExchange(buf);

                    AssertEmpty(buf);
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
                break;
            }

            case HandshakeType.certificate_request:
            {
                switch (this.mConnectionState)
                {
                case CS_SERVER_CERTIFICATE:
                case CS_CERTIFICATE_STATUS:
                case CS_SERVER_KEY_EXCHANGE:
                {
                    if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE)
                    {
                        // There was no server key exchange message; check it's OK
                        this.mKeyExchange.SkipServerKeyExchange();
                    }

                    if (this.mAuthentication == null)
                    {
                        /*
                         * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
                         * to request client identification.
                         */
                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
                    }

                    this.mCertificateRequest = CertificateRequest.Parse(Context, buf);

                    AssertEmpty(buf);

                    this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest);

                    /*
                     * TODO Give the client a chance to immediately select the CertificateVerify hash
                     * algorithm here to avoid tracking the other hash algorithms unnecessarily?
                     */
                    TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
                                                     this.mCertificateRequest.SupportedSignatureAlgorithms);

                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_CERTIFICATE_REQUEST;
                break;
            }

            case HandshakeType.session_ticket:
            {
                switch (this.mConnectionState)
                {
                case CS_CLIENT_FINISHED:
                {
                    if (!this.mExpectSessionTicket)
                    {
                        /*
                         * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
                         * SessionTicket extension in the ServerHello.
                         */
                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                    }

                    /*
                     * RFC 5077 3.4. If the client receives a session ticket from the server, then it
                     * discards any Session ID that was sent in the ServerHello.
                     */
                    InvalidateSession();

                    ReceiveNewSessionTicketMessage(buf);
                    break;
                }

                default:
                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                }

                this.mConnectionState = CS_SERVER_SESSION_TICKET;
                break;
            }

            case HandshakeType.hello_request:
            {
                AssertEmpty(buf);

                /*
                 * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the
                 * client is currently negotiating a session. This message may be ignored by the client
                 * if it does not wish to renegotiate a session, or the client may, if it wishes,
                 * respond with a no_renegotiation alert.
                 */
                if (this.mConnectionState == CS_END)
                {
                    RefuseRenegotiation();
                }
                break;
            }

            case HandshakeType.client_hello:
            case HandshakeType.client_key_exchange:
            case HandshakeType.certificate_verify:
            case HandshakeType.hello_verify_request:
            default:
                throw new TlsFatalAlert(AlertDescription.unexpected_message);
            }
        }
        protected virtual void ReceiveServerHelloMessage(MemoryStream buf)
        {
            {
                ProtocolVersion server_version = TlsUtilities.ReadVersion(buf);
                if (server_version.IsDtls)
                {
                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                }

                // Check that this matches what the server is Sending in the record layer
                if (!server_version.Equals(this.mRecordStream.ReadVersion))
                {
                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                }

                ProtocolVersion client_version = Context.ClientVersion;
                if (!server_version.IsEqualOrEarlierVersionOf(client_version))
                {
                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                }

                this.mRecordStream.SetWriteVersion(server_version);
                ContextAdmin.SetServerVersion(server_version);
                this.mTlsClient.NotifyServerVersion(server_version);

                if (HTTPManager.Logger.Level <= Loglevels.All)
                {
                    HTTPManager.Logger.Verbose("TlsClientProtocol", "ReceiveServerHelloMessage server_version: " + server_version.ToString(), this.LoggingContext);
                }
            }

            /*
             * Read the server random
             */
            this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf);

            this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf);
            if (this.mSelectedSessionID.Length > 32)
            {
                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
            }
            this.mTlsClient.NotifySessionID(this.mSelectedSessionID);
            this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null &&
                                   Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID);

            /*
             * Find out which CipherSuite the server has chosen and check that it was one of the offered
             * ones, and is a valid selection for the negotiated version.
             */
            int selectedCipherSuite = TlsUtilities.ReadUint16(buf);

            if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite) ||
                selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL ||
                CipherSuite.IsScsv(selectedCipherSuite) ||
                !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion))
            {
                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
            }

            if (HTTPManager.Logger.Level <= Loglevels.All)
            {
                HTTPManager.Logger.Verbose("TlsClientProtocol", "ReceiveServerHelloMessage selectedCipherSuite: " + selectedCipherSuite.ToString("X"), this.LoggingContext);
            }

            this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite);

            /*
             * Find out which CompressionMethod the server has chosen and check that it was one of the
             * offered ones.
             */
            byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf);

            if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod))
            {
                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
            }

            if (HTTPManager.Logger.Level <= Loglevels.All)
            {
                HTTPManager.Logger.Verbose("TlsClientProtocol", "ReceiveServerHelloMessage selectedCompressionMethod: " + selectedCompressionMethod.ToString(), this.LoggingContext);
            }

            this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);

            /*
             * RFC 3546 2.2 The extended server hello message format MAY be sent in place of the server
             * hello message when the client has requested extended functionality via the extended
             * client hello message specified in Section 2.1. ... Note that the extended server hello
             * message is only sent in response to an extended client hello message. This prevents the
             * possibility that the extended server hello message could "break" existing TLS 1.0
             * clients.
             */
            this.mServerExtensions = ReadExtensions(buf);

            /*
             * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended
             * master secret [..]. (and see 5.2, 5.3)
             */
            this.mSecurityParameters.extendedMasterSecret = !TlsUtilities.IsSsl(mTlsClientContext) &&
                                                            TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions);

            if (!mSecurityParameters.IsExtendedMasterSecret &&
                (mResumedSession || mTlsClient.RequiresExtendedMasterSecret()))
            {
                throw new TlsFatalAlert(AlertDescription.handshake_failure);
            }

            /*
             * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
             * extended client hello message.
             *
             * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
             * Hello is always allowed.
             */
            if (this.mServerExtensions != null)
            {
                foreach (int extType in this.mServerExtensions.Keys)
                {
                    /*
                     * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a
                     * ClientHello containing only the SCSV is an explicit exception to the prohibition
                     * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is
                     * only allowed because the client is signaling its willingness to receive the
                     * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
                     */
                    if (extType == ExtensionType.renegotiation_info)
                    {
                        continue;
                    }

                    /*
                     * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
                     * same extension type appeared in the corresponding ClientHello. If a client
                     * receives an extension type in ServerHello that it did not request in the
                     * associated ClientHello, it MUST abort the handshake with an unsupported_extension
                     * fatal alert.
                     */
                    if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType))
                    {
                        throw new TlsFatalAlert(AlertDescription.unsupported_extension);
                    }

                    /*
                     * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
                     * extensions appearing in the client hello, and Send a server hello containing no
                     * extensions[.]
                     */
                    if (this.mResumedSession)
                    {
                        // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats
                        // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats
                        // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats
                        //                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                    }

                    if (HTTPManager.Logger.Level <= Loglevels.All)
                    {
                        HTTPManager.Logger.Verbose("TlsClientProtocol", "ReceiveServerHelloMessage mServerExtensions: " + extType.ToString(), this.LoggingContext);
                    }
                }
            }

            /*
             * RFC 5746 3.4. Client Behavior: Initial Handshake
             */
            {
                /*
                 * When a ServerHello is received, the client MUST check if it includes the
                 * "renegotiation_info" extension:
                 */
                byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
                if (renegExtData != null)
                {
                    /*
                     * If the extension is present, set the secure_renegotiation flag to TRUE. The
                     * client MUST then verify that the length of the "renegotiated_connection"
                     * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal
                     * handshake_failure alert).
                     */
                    this.mSecureRenegotiation = true;

                    if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
                    {
                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
                    }
                }
            }

            // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming
            this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation);

            IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions;

            if (this.mResumedSession)
            {
                if (selectedCipherSuite != this.mSessionParameters.CipherSuite ||
                    selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm)
                {
                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                }

                sessionClientExtensions = null;
                sessionServerExtensions = this.mSessionParameters.ReadServerExtensions();
            }

            this.mSecurityParameters.cipherSuite          = selectedCipherSuite;
            this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;

            if (sessionServerExtensions != null && sessionServerExtensions.Count > 0)
            {
                {
                    /*
                     * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
                     * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
                     * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
                     * client.
                     */
                    bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions);
                    if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite))
                    {
                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                    }

                    this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
                }

                this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
                                                                                               sessionServerExtensions, AlertDescription.illegal_parameter);

                this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions);

                /*
                 * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
                 * a session resumption handshake.
                 */
                this.mAllowCertificateStatus = !this.mResumedSession &&
                                               TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request,
                                                                                          AlertDescription.illegal_parameter);

                this.mExpectSessionTicket = !this.mResumedSession &&
                                            TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket,
                                                                                       AlertDescription.illegal_parameter);
            }

            if (sessionClientExtensions != null)
            {
                this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
            }

            this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, this.mSecurityParameters.CipherSuite);

            /*
             * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify
             * verify_data_length has a verify_data_length equal to 12. This includes all
             * existing cipher suites.
             */
            this.mSecurityParameters.verifyDataLength = 12;
        }
        protected virtual void SendServerHelloMessage()
        {
            HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello);

            {
                ProtocolVersion server_version = mTlsServer.GetServerVersion();
                if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion))
                {
                    throw new TlsFatalAlert(AlertDescription.internal_error);
                }

                mRecordStream.ReadVersion = server_version;
                mRecordStream.SetWriteVersion(server_version);
                mRecordStream.SetRestrictReadVersion(true);
                ContextAdmin.SetServerVersion(server_version);

                TlsUtilities.WriteVersion(server_version, message);
            }

            message.Write(this.mSecurityParameters.serverRandom);

            /*
             * The server may return an empty session_id to indicate that the session will not be cached
             * and therefore cannot be resumed.
             */
            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message);

            int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite();

            if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite) ||
                selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL ||
                CipherSuite.IsScsv(selectedCipherSuite) ||
                !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion))
            {
                throw new TlsFatalAlert(AlertDescription.internal_error);
            }
            mSecurityParameters.cipherSuite = selectedCipherSuite;

            byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod();

            if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod))
            {
                throw new TlsFatalAlert(AlertDescription.internal_error);
            }
            mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;

            TlsUtilities.WriteUint16(selectedCipherSuite, message);
            TlsUtilities.WriteUint8(selectedCompressionMethod, message);

            this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mTlsServer.GetServerExtensions());

            /*
             * RFC 5746 3.6. Server Behavior: Initial Handshake
             */
            if (this.mSecureRenegotiation)
            {
                byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
                bool   noRenegExt   = (null == renegExtData);

                if (noRenegExt)
                {
                    /*
                     * Note that Sending a "renegotiation_info" extension in response to a ClientHello
                     * containing only the SCSV is an explicit exception to the prohibition in RFC 5246,
                     * Section 7.4.1.4, on the server Sending unsolicited extensions and is only allowed
                     * because the client is signaling its willingness to receive the extension via the
                     * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
                     */

                    /*
                     * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                     * "renegotiation_info" extension in the ServerHello message.
                     */
                    this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
                }
            }

            if (TlsUtilities.IsSsl(mTlsServerContext))
            {
                mSecurityParameters.extendedMasterSecret = false;
            }
            else if (mSecurityParameters.IsExtendedMasterSecret)
            {
                TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions);
            }

            /*
             * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
             * extensions appearing in the client hello, and Send a server hello containing no
             * extensions.
             */

            if (this.mServerExtensions.Count > 0)
            {
                this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions);

                this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions,
                                                                                               mServerExtensions, AlertDescription.internal_error);

                this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions);

                /*
                 * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
                 * a session resumption handshake.
                 */
                this.mAllowCertificateStatus = !mResumedSession &&
                                               TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request,
                                                                                          AlertDescription.internal_error);

                this.mExpectSessionTicket = !mResumedSession &&
                                            TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket,
                                                                                       AlertDescription.internal_error);

                WriteExtensions(message, this.mServerExtensions);
            }

            mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite);

            /*
             * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
             * a verify_data_length equal to 12. This includes all existing cipher suites.
             */
            mSecurityParameters.verifyDataLength = 12;

            ApplyMaxFragmentLengthExtension();

            message.WriteToRecordStream(this);
        }