protected SslHandshakeStatus ProcessServerHello(HandshakeMessage message)
 {
     if (m_State != HandshakeType.ClientHello && m_State != HandshakeType.HelloRequest)
     {
         throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message.");
     }
     UpdateHashes(message, HashUpdate.All);             // input message
     if (message.fragment.Length < 2 || message.fragment[0] != GetVersion().major || message.fragment[1] != GetVersion().minor)
     {
         throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client.");
     }
     try {
         // extract the time from the client [== 1 uint]
         m_ServerTime = new byte[4];
         Array.Copy(message.fragment, 2, m_ServerTime, 0, 4);
         // extract the random bytes [== 28 bytes]
         m_ServerRandom = new byte[28];
         Array.Copy(message.fragment, 6, m_ServerRandom, 0, 28);
         // extact the session ID [== 0..32 bytes]
         int length = message.fragment[34];
         if (length > 32)
         {
             throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes.");
         }
         m_SessionID = new byte[length];
         Array.Copy(message.fragment, 35, m_SessionID, 0, length);
         // extract the selected cipher suite
         m_EncryptionScheme = CipherSuites.GetCipherAlgorithmType(message.fragment, 35 + length);
         // extract the selected compression method
         m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithmType(message.fragment, 37 + length);
     } catch (Exception e) {
         throw new SslException(e, AlertDescription.InternalError, "The message is invalid.");
     }
     return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null));
 }
        protected override byte[] GetClientHello()
        {
            if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message.");
            }
            m_IsNegotiating = true;
            m_State         = HandshakeType.ClientHello;
            byte[]           ciphers = CipherSuites.GetCipherAlgorithmBytes(m_Options.AllowedAlgorithms);
            byte[]           compr   = CompressionAlgorithm.GetCompressionAlgorithmBytes(m_Options.AllowedAlgorithms);
            HandshakeMessage temp    = new HandshakeMessage(HandshakeType.ClientHello, new byte[38 + ciphers.Length + compr.Length]);

            m_ClientTime   = GetUnixTime();
            m_ClientRandom = new byte[28];
            m_RNG.GetBytes(m_ClientRandom);
            ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol);

            temp.fragment[0] = pv.major;
            temp.fragment[1] = pv.minor;
            Array.Copy(m_ClientTime, 0, temp.fragment, 2, 4);
            Array.Copy(m_ClientRandom, 0, temp.fragment, 6, 28);
            temp.fragment[34] = 0;             // do not resume a session, and do not let the other side cache it
            temp.fragment[35] = (byte)(ciphers.Length / 256);
            temp.fragment[36] = (byte)(ciphers.Length % 256);
            Array.Copy(ciphers, 0, temp.fragment, 37, ciphers.Length);
            temp.fragment[37 + ciphers.Length] = (byte)compr.Length;
            Array.Copy(compr, 0, temp.fragment, 38 + ciphers.Length, compr.Length);
            byte[] ret = temp.ToBytes();
            UpdateHashes(ret, HashUpdate.All);             // client hello message
            return(m_RecordLayer.EncryptBytes(ret, 0, ret.Length, ContentType.Handshake));
        }
        protected SslHandshakeStatus ProcessServerHello(HandshakeMessage message)
        {
            if (m_State != HandshakeType.ClientHello && m_State != HandshakeType.HelloRequest)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message.");
            }

            UpdateHashes(message, HashUpdate.All); // input message

            //Violation with tls spec. If remote side uses higher protocol version than your, then we must answer with your highest version
            //if (message.fragment.Length < 2 || message.fragment[0] != GetVersion().major || message.fragment[1] != GetVersion().minor)
            //throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client.");

            int currentProcessedLen = 2;

            try {
                // extract the time from the client [== 1 uint]
                m_ServerTime = new byte[4];
                Buffer.BlockCopy(message.fragment, currentProcessedLen, m_ServerTime, 0, 4);
                currentProcessedLen += 4;

                // extract the random bytes [== 28 bytes]
                m_ServerRandom = new byte[28];
                Buffer.BlockCopy(message.fragment, currentProcessedLen, m_ServerRandom, 0, 28);
                currentProcessedLen += 28;

                // extact the session ID [== 0..32 bytes]
                int length = message.fragment[currentProcessedLen++];
                if (length > 32)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes.");
                }

                m_SessionID = new byte[length];
                Buffer.BlockCopy(message.fragment, currentProcessedLen, m_SessionID, 0, length);
                currentProcessedLen += length;

                // extract the selected cipher suite
                m_EncryptionScheme   = CipherSuites.GetCipherAlgorithmType(message.fragment, currentProcessedLen);
                currentProcessedLen += 2;

                // extract the selected compression method
                m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithmType(message.fragment, currentProcessedLen++);
            }
            catch (Exception e)
            {
                throw new SslException(e, AlertDescription.InternalError, "The message is invalid.");
            }

            if (message.fragment.Length == currentProcessedLen)
            {
                return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null));
            }

            this.ProcessExtensions(message.fragment, ref currentProcessedLen, ConnectionEnd.Client);

            return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null));
        }
        protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message)
        {
            if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message.");
            }
            m_IsNegotiating = true;
            UpdateHashes(message, HashUpdate.All);             // input message
            // process ClientHello
            ProtocolVersion pv = new ProtocolVersion(message.fragment[0], message.fragment[1]);

            m_MaxClientVersion = pv;
            if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt())
            {
                throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client.");
            }
            try {
                // extract the time from the client [== 1 uint]
                m_ClientTime = new byte[4];
                Array.Copy(message.fragment, 2, m_ClientTime, 0, 4);
                // extract the random bytes [== 28 bytes]
                m_ClientRandom = new byte[28];
                Array.Copy(message.fragment, 6, m_ClientRandom, 0, 28);
                // extact the session ID [== 0..32 bytes]
                int length = message.fragment[34];
                if (length > 32)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes.");
                }
                m_SessionID = new byte[length];
                Array.Copy(message.fragment, 35, m_SessionID, 0, length);
                // extract the available cipher suites
                length += 35;
                int ciphers_size = message.fragment[length] * 256 + message.fragment[length + 1];
                if (ciphers_size < 2 || ciphers_size % 2 != 0)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even.");
                }
                byte[] ciphers = new byte[ciphers_size];
                Array.Copy(message.fragment, length + 2, ciphers, 0, ciphers_size);
                m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms);
                // extract the available compression algorithms
                length += ciphers_size + 2;
                int compressors_size = message.fragment[length];
                if (compressors_size == 0)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "No compressor specified.");
                }
                byte[] compressors = new byte[compressors_size];
                Array.Copy(message.fragment, length + 1, compressors, 0, compressors_size);
                m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms);
            } catch (Exception e) {
                throw new SslException(e, AlertDescription.InternalError, "The message is invalid.");
            }
            // create reply
            return(GetClientHelloResult());
        }
Example #5
0
 protected void GenerateCiphers(byte[] premaster)
 {
     byte[] clientrnd = new byte[32], serverrnd = new byte[32];
     byte[] random    = new byte[64];
     Array.Copy(m_ClientTime, 0, clientrnd, 0, 4);
     Array.Copy(m_ClientRandom, 0, clientrnd, 4, 28);
     Array.Copy(m_ServerTime, 0, serverrnd, 0, 4);
     Array.Copy(m_ServerRandom, 0, serverrnd, 4, 28);
     m_MasterSecret = GenerateMasterSecret(premaster, clientrnd, serverrnd);
     m_CipherSuite  = CipherSuites.GetCipherSuite(GetProtocol(), m_MasterSecret, clientrnd, serverrnd, m_EncryptionScheme, m_Options.Entity);
     Array.Clear(premaster, 0, premaster.Length);
 }
        protected SslHandshakeStatus ProcessServerKeyExchange(HandshakeMessage message)
        {
            if (m_State != HandshakeType.Certificate)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ServerKeyExchange message must be preceded by a Certificate message.");
            }
            CipherDefinition cd = CipherSuites.GetCipherDefinition(m_EncryptionScheme);

            if (!cd.Exportable)
            {
                throw new SslException(AlertDescription.HandshakeFailure, "The ServerKeyExchange message should not be sent for non-exportable ciphers.");
            }
            if (m_RemoteCertificate.GetPublicKeyLength() <= 512)
            {
                throw new SslException(AlertDescription.HandshakeFailure, "The ServerKeyExchange message should not be sent because the server certificate public key is of exportable length.");
            }
            UpdateHashes(message, HashUpdate.All);             // input message
            // extract modulus and exponent
            RSAParameters pars = new RSAParameters();
            int           size = message.fragment[0] * 256 + message.fragment[1];

            pars.Modulus = new byte[size];
            Buffer.BlockCopy(message.fragment, 2, pars.Modulus, 0, size);
            int offset = size + 2;

            size          = message.fragment[offset] * 256 + message.fragment[offset + 1];
            pars.Exponent = new byte[size];
            Buffer.BlockCopy(message.fragment, offset + 2, pars.Exponent, 0, size);
            offset       += size + 2;
            pars.Modulus  = RemoveLeadingZeros(pars.Modulus);
            pars.Exponent = RemoveLeadingZeros(pars.Exponent);
            m_KeyCipher   = new RSACryptoServiceProvider();
            m_KeyCipher.ImportParameters(pars);
            // compute verification hashes
            MD5SHA1CryptoServiceProvider ms = new MD5SHA1CryptoServiceProvider();

            ms.TransformBlock(m_ClientTime, 0, m_ClientTime.Length, m_ClientTime, 0);
            ms.TransformBlock(m_ClientRandom, 0, m_ClientRandom.Length, m_ClientRandom, 0);
            ms.TransformBlock(m_ServerTime, 0, m_ServerTime.Length, m_ServerTime, 0);
            ms.TransformBlock(m_ServerRandom, 0, m_ServerRandom.Length, m_ServerRandom, 0);
            ms.TransformFinalBlock(message.fragment, 0, offset);
            // verify the signature
            size = message.fragment[offset] * 256 + message.fragment[offset + 1];
            byte[] signature = new byte[size];             // holds the signature returned by the server
            Buffer.BlockCopy(message.fragment, offset + 2, signature, 0, size);
            if (!ms.VerifySignature(m_RemoteCertificate, signature))
            {
                throw new SslException(AlertDescription.HandshakeFailure, "The data was not signed by the server certificate.");
            }
            ms.Clear();
            return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null));
        }
        // Thanks to Brandon for notifying us about a bug in this method
        public override SslHandshakeStatus ProcessSsl2Hello(byte[] hello)
        {
            if (m_State != HandshakeType.Nothing)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "SSL2 ClientHello message must be the first message.");
            }
            m_IsNegotiating = true;
            m_State         = HandshakeType.ClientHello;
            UpdateHashes(hello, HashUpdate.All);             // input message
            // process ClientHello
            ProtocolVersion pv = new ProtocolVersion(hello[1], hello[2]);

            m_MaxClientVersion = pv;
            if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt())
            {
                throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client.");
            }
            int csl  = hello[3] * 256 + hello[4];           // cipher spec length
            int sidl = hello[5] * 256 + hello[6];           // session id length
            int cl   = hello[7] * 256 + hello[8];           // challenge length

            // process ciphers
            byte[] ciphers = new byte[(csl / 3) * 2];
            int    offset  = 10;

            for (int i = 0; i < ciphers.Length; i += 2)
            {
                Array.Copy(hello, offset, ciphers, i, 2);
                offset += 3;
            }
            m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms);
            // process session id
            m_SessionID = new byte[sidl];
            Array.Copy(hello, 9 + csl, m_SessionID, 0, sidl);
            // process random data [challenge]
            m_ClientTime   = new byte[4];
            m_ClientRandom = new byte[28];
            if (cl <= 28)
            {
                Array.Copy(hello, 9 + csl + sidl, m_ClientRandom, m_ClientRandom.Length - cl, cl);
            }
            else
            {
                Array.Copy(hello, 9 + csl + sidl + (cl - 28), m_ClientRandom, 0, 28);
                Array.Copy(hello, 9 + csl + sidl, m_ClientTime, 4 - (cl - 28), cl - 28);
            }
            m_CompressionMethod = SslAlgorithms.NULL_COMPRESSION;
            return(GetClientHelloResult());
        }
        protected override byte[] GetClientHello()
        {
            if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message.");
            }

            m_IsNegotiating = true;
            m_State         = HandshakeType.ClientHello;
            byte[] ciphers = CipherSuites.GetCipherAlgorithmBytes(m_Options.AllowedAlgorithms);
            byte[] compr   = CompressionAlgorithm.GetCompressionAlgorithmBytes(m_Options.AllowedAlgorithms);

            MemoryStream clientHello = new MemoryStream();

            m_ClientTime   = GetUnixTime();
            m_ClientRandom = new byte[28];
            m_RNG.GetBytes(m_ClientRandom);

            ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol);

            clientHello.WriteByte(pv.major);
            clientHello.WriteByte(pv.minor);
            clientHello.Write(m_ClientTime, 0, m_ClientTime.Length);
            clientHello.Write(m_ClientRandom, 0, m_ClientRandom.Length);
            clientHello.WriteByte(0);
            clientHello.WriteByte((byte)(ciphers.Length / 256));
            clientHello.WriteByte((byte)(ciphers.Length % 256));
            clientHello.Write(ciphers, 0, ciphers.Length);
            clientHello.WriteByte((byte)compr.Length);
            clientHello.Write(compr, 0, compr.Length);

            if (this.clientHelloExts != null)
            {
                this.clientHelloExts.WriteExtensions(clientHello, ConnectionEnd.Client);
            }

            HandshakeMessage hm = new HandshakeMessage(HandshakeType.ClientHello, clientHello.ToArray());

            byte[] ret = hm.ToBytes();
            UpdateHashes(ret, HashUpdate.All);             // client hello message
            return(m_RecordLayer.EncryptBytes(ret, 0, ret.Length, ContentType.Handshake));
        }
        protected SslHandshakeStatus ProcessCertificateRequest(HandshakeMessage message)
        {
            if (m_State == HandshakeType.ServerKeyExchange)
            {
                CipherDefinition cd = CipherSuites.GetCipherDefinition(m_EncryptionScheme);
                if (this.m_RemoteCertificate.GetPublicKeyLength() <= 512 || !cd.Exportable)
                {
                    throw new SslException(AlertDescription.HandshakeFailure, "Invalid message.");
                }
            }
            else if (m_State != HandshakeType.Certificate)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "CertificateRequest message must be preceded by a Certificate or ServerKeyExchange message.");
            }
            UpdateHashes(message, HashUpdate.All);             // input message
            // get supported certificate types
            bool supportsRsaCerts = false;

            byte[] certTypes = new byte[message.fragment[0]];             // currently we're not doing anything with the supported certificate types
            Buffer.BlockCopy(message.fragment, 1, certTypes, 0, certTypes.Length);
            for (int i = 0; i < certTypes.Length; i++)
            {
                if (certTypes[i] == 1)                   // rsa_sign
                {
                    supportsRsaCerts = true;
                    break;
                }
            }
            // get list of distinguished names
            if (m_Options.RequestHandler != null && supportsRsaCerts)               // make sure the client passed a delegate
            {
                Queue q = new Queue();
                DistinguishedNameList r = new DistinguishedNameList();
                int    size, offset = message.fragment[0] + 3;
                byte[] buffer;
                while (offset < message.fragment.Length)
                {
                    size   = message.fragment[offset] * 256 + message.fragment[offset + 1];
                    buffer = new byte[size];
                    Buffer.BlockCopy(message.fragment, offset + 2, buffer, 0, size);
                    q.Enqueue(buffer);
                    offset += size + 2;
                }
                // decode RDN structures
                while (q.Count > 0)
                {
                    r.Add(ProcessName((byte[])q.Dequeue()));
                }
                RequestEventArgs e = new RequestEventArgs();
                try {
                    m_Options.RequestHandler(Parent, r, e);
                    if (e.Certificate != null)
                    {
                        m_Options.Certificate = e.Certificate;
                    }
                } catch (Exception de) {
                    throw new SslException(de, AlertDescription.InternalError, "The code in the CertRequestEventHandler delegate threw an error.");
                }
            }
            if (!supportsRsaCerts)
            {
                m_Options.Certificate = null;                 // do not send client certificate
            }
            m_MutualAuthentication = true;
            return(new SslHandshakeStatus(SslStatus.MessageIncomplete, null));
        }
        protected SslHandshakeStatus GetClientHelloResult()
        {
            MemoryStream       retMessage = new MemoryStream();
            SslHandshakeStatus ret        = new SslHandshakeStatus();
            HandshakeMessage   temp;

            byte[] bytes;
            // ServerHello message
            temp           = new HandshakeMessage(HandshakeType.ServerHello, new byte[38]);
            m_ServerTime   = GetUnixTime();
            m_ServerRandom = new byte[28];
            m_RNG.GetBytes(m_ServerRandom);
            temp.fragment[0] = GetVersion().major;
            temp.fragment[1] = GetVersion().minor;
            Array.Copy(m_ServerTime, 0, temp.fragment, 2, 4);
            Array.Copy(m_ServerRandom, 0, temp.fragment, 6, 28);
            temp.fragment[34] = 0;             // do not resume a session, and do not let the other side cache it
            Array.Copy(CipherSuites.GetCipherAlgorithmBytes(m_EncryptionScheme), 0, temp.fragment, 35, 2);
            temp.fragment[37] = CompressionAlgorithm.GetAlgorithmByte(m_CompressionMethod);
            bytes             = temp.ToBytes();
            retMessage.Write(bytes, 0, bytes.Length);
            // Certificate message
            byte[] certs = GetCertificateList(m_Options.Certificate);
            temp.type     = HandshakeType.Certificate;
            temp.fragment = certs;
            bytes         = temp.ToBytes();
            retMessage.Write(bytes, 0, bytes.Length);
            // ServerKeyExchange message [optional] => only with RSA_EXPORT and public key > 512 bits
            if (m_Options.Certificate.GetPublicKeyLength() > 512 && CipherSuites.GetCipherDefinition(m_EncryptionScheme).Exportable)
            {
                MemoryStream kes = new MemoryStream();
                MD5SHA1CryptoServiceProvider mscsp = new MD5SHA1CryptoServiceProvider();
                // hash the client and server random values
                mscsp.TransformBlock(m_ClientTime, 0, 4, m_ClientTime, 0);
                mscsp.TransformBlock(m_ClientRandom, 0, 28, m_ClientRandom, 0);
                mscsp.TransformBlock(m_ServerTime, 0, 4, m_ServerTime, 0);
                mscsp.TransformBlock(m_ServerRandom, 0, 28, m_ServerRandom, 0);
                // create a new 512 bit RSA key
                m_KeyCipher = new RSACryptoServiceProvider(512);
                RSAParameters p = m_KeyCipher.ExportParameters(false);
                // write the key parameters to the output stream
                bytes = new byte[] { (byte)(p.Modulus.Length / 256), (byte)(p.Modulus.Length % 256) };
                kes.Write(bytes, 0, 2);
                kes.Write(p.Modulus, 0, p.Modulus.Length);
                mscsp.TransformBlock(bytes, 0, 2, bytes, 0);
                mscsp.TransformBlock(p.Modulus, 0, p.Modulus.Length, p.Modulus, 0);
                bytes = new byte[] { (byte)(p.Exponent.Length / 256), (byte)(p.Exponent.Length % 256) };
                kes.Write(bytes, 0, 2);
                kes.Write(p.Exponent, 0, p.Exponent.Length);
                mscsp.TransformBlock(bytes, 0, 2, bytes, 0);
                mscsp.TransformFinalBlock(p.Exponent, 0, p.Exponent.Length);
                // create signature
                bytes = mscsp.CreateSignature(m_Options.Certificate);
                kes.Write(new byte[] { (byte)(bytes.Length / 256), (byte)(bytes.Length % 256) }, 0, 2);
                kes.Write(bytes, 0, bytes.Length);
                // write to output
                temp.type     = HandshakeType.ServerKeyExchange;
                temp.fragment = kes.ToArray();
                bytes         = temp.ToBytes();
                retMessage.Write(bytes, 0, bytes.Length);
                kes.Close();
            }
            else
            {
                m_KeyCipher = (RSACryptoServiceProvider)m_Options.Certificate.PrivateKey;
            }
            // CertificateRequest message [optional]
            if (m_MutualAuthentication)
            {
                bytes = GetDistinguishedNames();
                if (bytes.Length != 0)                   // make sure at least one certificate is returned
                {
                    temp.type        = HandshakeType.CertificateRequest;
                    temp.fragment    = new byte[bytes.Length + 4];
                    temp.fragment[0] = 1;                     // one certificate type supported
                    temp.fragment[1] = 1;                     // cert type RSA
                    temp.fragment[2] = (byte)(bytes.Length / 256);
                    temp.fragment[3] = (byte)(bytes.Length % 256);
                    Array.Copy(bytes, 0, temp.fragment, 4, bytes.Length);
                    bytes = temp.ToBytes();
                    retMessage.Write(bytes, 0, bytes.Length);
                }
            }
            // ServerHelloDone message
            temp.type     = HandshakeType.ServerHelloDone;
            temp.fragment = new byte[0];
            bytes         = temp.ToBytes();
            retMessage.Write(bytes, 0, bytes.Length);
            // final adjustments
            ret.Status  = SslStatus.ContinueNeeded;
            ret.Message = retMessage.ToArray();
            retMessage.Close();
            UpdateHashes(ret.Message, HashUpdate.All);             // output message
            ret.Message = m_RecordLayer.EncryptBytes(ret.Message, 0, ret.Message.Length, ContentType.Handshake);
            return(ret);
        }
Example #11
0
        protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message)
        {
            if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished)
            {
                throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message.");
            }

            Alert alert = null;

            m_IsNegotiating = true;
            UpdateHashes(message, HashUpdate.All);             // input message
            // process ClientHello

            int currentLen = 0;

            ProtocolVersion pv = new ProtocolVersion(message.fragment[currentLen++], message.fragment[currentLen++]);

            m_MaxClientVersion = pv;

            //Violation with tls spec. If remote side uses higher protocol version than your, then we must answer with your highest version
            //if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt())
            //	throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client.");

            try {
                // extract the time from the client [== 1 uint]
                m_ClientTime = new byte[4];
                Buffer.BlockCopy(message.fragment, currentLen, m_ClientTime, 0, 4);
                currentLen += 4;

                // extract the random bytes [== 28 bytes]
                m_ClientRandom = new byte[28];
                Buffer.BlockCopy(message.fragment, currentLen, m_ClientRandom, 0, 28);
                currentLen += 28;

                // extact the session ID [== 0..32 bytes]
                byte length = message.fragment[currentLen++];
                if (length > 32)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes.");
                }

                m_SessionID = new byte[length];
                Buffer.BlockCopy(message.fragment, currentLen, m_SessionID, 0, length);
                currentLen += length;

                // extract the available cipher suites
                Int16 ciphers_size = BinaryHelper.Int16FromBytes(message.fragment[currentLen++], message.fragment[currentLen++]);//message.fragment[length] * 256 + message.fragment[length + 1];
                if (ciphers_size < 2 || ciphers_size % 2 != 0)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even.");
                }

                byte[] ciphers = new byte[ciphers_size];
                Buffer.BlockCopy(message.fragment, currentLen, ciphers, 0, ciphers_size);
                currentLen += ciphers_size;

                m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms);
                // extract the available compression algorithms

                int compressors_size = message.fragment[currentLen++];
                if (compressors_size == 0)
                {
                    throw new SslException(AlertDescription.IllegalParameter, "No compressor specified.");
                }

                byte[] compressors = new byte[compressors_size];
                Buffer.BlockCopy(message.fragment, currentLen, compressors, 0, compressors_size);
                m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms);
                currentLen         += compressors_size;
                try
                {
                    if (message.fragment.Length > currentLen)
                    {
                        this.ProcessExtensions(message.fragment, ref currentLen, ConnectionEnd.Server);
                    }
                }
                catch (ALPNCantSelectProtocolException ex)
                {
                    alert = new Alert(ex.Description, ex.Level);
                }
            } catch (Exception e) {
                throw new SslException(e, AlertDescription.InternalError, "The message is invalid.");
            }
            // create reply
            return(GetClientHelloResult(alert));
        }