Пример #1
0
        public virtual void Connect(TlsClient tlsClient)
        {
            //IL_0008: Unknown result type (might be due to invalid IL or missing references)
            //IL_001b: Unknown result type (might be due to invalid IL or missing references)
            if (tlsClient == null)
            {
                throw new ArgumentNullException("tlsClient");
            }
            if (mTlsClient != null)
            {
                throw new InvalidOperationException("'Connect' can only be called once");
            }
            mTlsClient                       = tlsClient;
            mSecurityParameters              = new SecurityParameters();
            mSecurityParameters.entity       = 1;
            mTlsClientContext                = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);
            mSecurityParameters.clientRandom = TlsProtocol.CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), mTlsClientContext.NonceRandomGenerator);
            mTlsClient.Init(mTlsClientContext);
            mRecordStream.Init(mTlsClientContext);
            TlsSession sessionToResume = tlsClient.GetSessionToResume();

            if (sessionToResume != null && sessionToResume.IsResumable)
            {
                SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
                if (sessionParameters != null)
                {
                    mTlsSession        = sessionToResume;
                    mSessionParameters = sessionParameters;
                }
            }
            SendClientHelloMessage();
            mConnectionState = 1;
            BlockForHandshake();
        }
        public virtual void Connect(TlsClient tlsClient)
        {
            if (tlsClient == null)
            {
                throw new ArgumentNullException("tlsClient");
            }
            if (this.mTlsClient != null)
            {
                throw new InvalidOperationException("'Connect' can only be called once");
            }
            this.mTlsClient                       = tlsClient;
            base.mSecurityParameters              = new SecurityParameters();
            base.mSecurityParameters.entity       = 1;
            this.mTlsClientContext                = new TlsClientContextImpl(base.mSecureRandom, base.mSecurityParameters);
            base.mSecurityParameters.clientRandom = TlsProtocol.CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), this.mTlsClientContext.NonceRandomGenerator);
            this.mTlsClient.Init(this.mTlsClientContext);
            base.mRecordStream.Init(this.mTlsClientContext);
            TlsSession sessionToResume = tlsClient.GetSessionToResume();

            if ((sessionToResume != null) && sessionToResume.IsResumable)
            {
                SessionParameters parameters = sessionToResume.ExportSessionParameters();
                if (parameters != null)
                {
                    base.mTlsSession        = sessionToResume;
                    base.mSessionParameters = parameters;
                }
            }
            this.SendClientHelloMessage();
            base.mConnectionState = 1;
            this.BlockForHandshake();
        }
Пример #3
0
        protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client)
        {
            //IL_0000: Unknown result type (might be due to invalid IL or missing references)
            //IL_0006: Expected O, but got Unknown
            MemoryStream    val           = new MemoryStream();
            ProtocolVersion clientVersion = client.ClientVersion;

            if (!clientVersion.IsDtls)
            {
                throw new TlsFatalAlert(80);
            }
            TlsClientContextImpl clientContext = state.clientContext;

            clientContext.SetClientVersion(clientVersion);
            TlsUtilities.WriteVersion(clientVersion, (Stream)(object)val);
            SecurityParameters securityParameters = clientContext.SecurityParameters;

            ((Stream)val).Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);
            byte[] array = TlsUtilities.EmptyBytes;
            if (state.tlsSession != null)
            {
                array = state.tlsSession.SessionID;
                if (array == null || array.Length > 32)
                {
                    array = TlsUtilities.EmptyBytes;
                }
            }
            TlsUtilities.WriteOpaque8(array, (Stream)(object)val);
            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, (Stream)(object)val);
            bool isFallback = client.IsFallback;

            state.offeredCipherSuites = client.GetCipherSuites();
            state.clientExtensions    = client.GetClientExtensions();
            byte[] extensionData = TlsUtilities.GetExtensionData(state.clientExtensions, 65281);
            bool   flag          = null == extensionData;
            bool   flag2         = !Arrays.Contains(state.offeredCipherSuites, 255);

            if (flag && flag2)
            {
                state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, 255);
            }
            if (isFallback && !Arrays.Contains(state.offeredCipherSuites, 22016))
            {
                state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, 22016);
            }
            TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, (Stream)(object)val);
            byte[] array2 = (state.offeredCompressionMethods = new byte[1]);
            TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, (Stream)(object)val);
            if (state.clientExtensions != null)
            {
                TlsProtocol.WriteExtensions((Stream)(object)val, state.clientExtensions);
            }
            return(val.ToArray());
        }
Пример #4
0
        protected virtual byte[] GenerateClientHello(DtlsClientProtocol.ClientHandshakeState state, TlsClient client)
        {
            MemoryStream    memoryStream  = new MemoryStream();
            ProtocolVersion clientVersion = client.ClientVersion;

            if (!clientVersion.IsDtls)
            {
                throw new TlsFatalAlert(80);
            }
            TlsClientContextImpl clientContext = state.clientContext;

            clientContext.SetClientVersion(clientVersion);
            TlsUtilities.WriteVersion(clientVersion, memoryStream);
            SecurityParameters securityParameters = clientContext.SecurityParameters;

            memoryStream.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);
            byte[] array = TlsUtilities.EmptyBytes;
            if (state.tlsSession != null)
            {
                array = state.tlsSession.SessionID;
                if (array == null || array.Length > 32)
                {
                    array = TlsUtilities.EmptyBytes;
                }
            }
            TlsUtilities.WriteOpaque8(array, memoryStream);
            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, memoryStream);
            bool isFallback = client.IsFallback;

            state.offeredCipherSuites = client.GetCipherSuites();
            state.clientExtensions    = client.GetClientExtensions();
            byte[] extensionData = TlsUtilities.GetExtensionData(state.clientExtensions, 65281);
            bool   flag          = null == extensionData;
            bool   flag2         = !Arrays.Contains(state.offeredCipherSuites, 255);

            if (flag && flag2)
            {
                state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, 255);
            }
            if (isFallback && !Arrays.Contains(state.offeredCipherSuites, 22016))
            {
                state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, 22016);
            }
            TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, memoryStream);
            byte[] offeredCompressionMethods = new byte[1];
            state.offeredCompressionMethods = offeredCompressionMethods;
            TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, memoryStream);
            if (state.clientExtensions != null)
            {
                TlsProtocol.WriteExtensions(memoryStream, state.clientExtensions);
            }
            return(memoryStream.ToArray());
        }
Пример #5
0
        protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
        {
            TlsClientContextImpl clientContext        = state.clientContext;
            ProtocolVersion      currentServerVersion = clientContext.ServerVersion;

            if (null == currentServerVersion)
            {
                clientContext.SetServerVersion(server_version);
                state.client.NotifyServerVersion(server_version);
            }
            else if (!currentServerVersion.Equals(server_version))
            {
                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
            }
        }
Пример #6
0
        protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
        {
            TlsClientContextImpl clientContext = state.clientContext;
            ProtocolVersion      serverVersion = clientContext.ServerVersion;

            if (serverVersion == null)
            {
                clientContext.SetServerVersion(server_version);
                state.client.NotifyServerVersion(server_version);
            }
            else if (!serverVersion.Equals(server_version))
            {
                throw new TlsFatalAlert(47);
            }
        }
Пример #7
0
        /**
         * Initiates a TLS handshake in the role of client.<br/>
         * <br/>
         * In blocking mode, this will not return until the handshake is complete.
         * In non-blocking mode, use {@link TlsPeer#NotifyHandshakeComplete()} to
         * receive a callback when the handshake is complete.
         *
         * @param tlsClient The {@link TlsClient} to use for the handshake.
         * @throws IOException If in blocking mode and handshake was not successful.
         */
        public virtual void Connect(TlsClient tlsClient)
        {
            if (tlsClient == null)
            {
                throw new ArgumentNullException("tlsClient");
            }
            if (this.mTlsClient != null)
            {
                throw new InvalidOperationException("'Connect' can only be called once");
            }

            this.mTlsClient = tlsClient;

            this.mSecurityParameters        = new SecurityParameters();
            this.mSecurityParameters.entity = ConnectionEnd.client;

            this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);

            this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
                                                                      mTlsClientContext.NonceRandomGenerator);

            this.mTlsClient.Init(mTlsClientContext);
            this.mRecordStream.Init(mTlsClientContext);

            tlsClient.NotifyCloseHandle(this);

            TlsSession sessionToResume = tlsClient.GetSessionToResume();

            if (sessionToResume != null && sessionToResume.IsResumable)
            {
                SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
                if (sessionParameters != null && sessionParameters.IsExtendedMasterSecret)
                {
                    this.mTlsSession        = sessionToResume;
                    this.mSessionParameters = sessionParameters;
                }
            }

            SendClientHelloMessage();
            this.mConnectionState = CS_CLIENT_HELLO;

            BlockForHandshake();
        }
        /**
         * Initiates a TLS handshake in the role of client
         *
         * @param tlsClient The {@link TlsClient} to use for the handshake.
         * @throws IOException If handshake was not successful.
         */
        public virtual void Connect(TlsClient tlsClient)
        {
            if (tlsClient == null)
                throw new ArgumentNullException("tlsClient");
            if (this.mTlsClient != null)
                throw new InvalidOperationException("'Connect' can only be called once");

            this.mTlsClient = tlsClient;

            this.mSecurityParameters = new SecurityParameters();
            this.mSecurityParameters.entity = ConnectionEnd.client;

            this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);

            this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
                mTlsClientContext.NonceRandomGenerator);

            this.mTlsClient.Init(mTlsClientContext);
            this.mRecordStream.Init(mTlsClientContext);

            TlsSession sessionToResume = tlsClient.GetSessionToResume();
            if (sessionToResume != null && sessionToResume.IsResumable)
            {
                SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
                if (sessionParameters != null)
                {
                    this.mTlsSession = sessionToResume;
                    this.mSessionParameters = sessionParameters;
                }
            }

            SendClientHelloMessage();
            this.mConnectionState = CS_CLIENT_HELLO;

            CompleteHandshake();
        }
Пример #9
0
        protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client)
        {
            MemoryStream buf = new MemoryStream();

            ProtocolVersion client_version = client.ClientVersion;

            if (!client_version.IsDtls)
            {
                throw new TlsFatalAlert(AlertDescription.internal_error);
            }

            TlsClientContextImpl context = state.clientContext;

            context.SetClientVersion(client_version);
            TlsUtilities.WriteVersion(client_version, buf);

            SecurityParameters securityParameters = context.SecurityParameters;

            buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);

            // Session ID
            byte[] session_id = TlsUtilities.EmptyBytes;
            if (state.tlsSession != null)
            {
                session_id = state.tlsSession.SessionID;
                if (session_id == null || session_id.Length > 32)
                {
                    session_id = TlsUtilities.EmptyBytes;
                }
            }
            TlsUtilities.WriteOpaque8(session_id, buf);

            // Cookie
            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf);

            bool fallback = client.IsFallback;

            /*
             * Cipher suites
             */
            state.offeredCipherSuites = client.GetCipherSuites();

            // Integer -> byte[]
            state.clientExtensions = client.GetClientExtensions();

            securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions);

            // Cipher Suites (and SCSV)
            {
                /*
                 * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
                 * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
                 * ClientHello. Including both is NOT RECOMMENDED.
                 */
                byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info);
                bool   noRenegExt   = (null == renegExtData);

                bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);

                if (noRenegExt && noRenegSCSV)
                {
                    // TODO Consider whether to default to a client extension instead
                    state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
                }

                /*
                 * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version
                 * containing a lower value than the latest (highest-valued) version supported by the
                 * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in
                 * ClientHello.cipher_suites.
                 */
                if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
                {
                    state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
                }

                TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
            }

            // TODO Add support for compression
            // Compression methods
            // state.offeredCompressionMethods = client.getCompressionMethods();
            state.offeredCompressionMethods = new byte[] { CompressionMethod.cls_null };

            TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);

            // Extensions
            if (state.clientExtensions != null)
            {
                TlsProtocol.WriteExtensions(buf, state.clientExtensions);
            }

            return(buf.ToArray());
        }
        public virtual void Connect(TlsClient tlsClient)
        {
            if (tlsClient == null)
                throw new ArgumentNullException("tlsClient");
            if (this.tlsClient != null)
                throw new InvalidOperationException("Connect can only be called once");

            /*
             * Send Client hello
             *
             * First, generate some random data.
             */
            this.securityParameters = new SecurityParameters();
            this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), random,
                ExporterLabel.client_random);

            this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
            this.tlsClient = tlsClient;
            this.tlsClient.Init(tlsClientContext);

            MemoryStream outStr = new MemoryStream();
            TlsUtilities.WriteVersion(outStr);
            outStr.Write(securityParameters.clientRandom, 0, 32);

            /*
            * Length of Session id
            */
            TlsUtilities.WriteUint8(0, outStr);

            this.offeredCipherSuites = this.tlsClient.GetCipherSuites();

            // Int32 -> byte[]
            this.clientExtensions = this.tlsClient.GetClientExtensions();

            // Cipher Suites (and SCSV)
            {
                /*
                 * RFC 5746 3.4.
                 * The client MUST include either an empty "renegotiation_info"
                 * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
                 * cipher suite value in the ClientHello.  Including both is NOT
                 * RECOMMENDED.
                 */
                bool noRenegExt = clientExtensions == null
                    || !clientExtensions.Contains(ExtensionType.renegotiation_info);

                int count = offeredCipherSuites.Length;
                if (noRenegExt)
                {
                    // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
                    ++count;
                }

                TlsUtilities.WriteUint16(2 * count, outStr);

                for (int i = 0; i < offeredCipherSuites.Length; ++i)
                {
                    TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
                }

                if (noRenegExt)
                {
                    TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
                }
            }

            /*
             * Compression methods, just the null method.
             */
            this.offeredCompressionMethods = tlsClient.GetCompressionMethods();

            {
                TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
                for (int i = 0; i < offeredCompressionMethods.Length; ++i)
                {
                    TlsUtilities.WriteUint8(offeredCompressionMethods[i], outStr);
                }
            }

            // Extensions
            if (clientExtensions != null)
            {
                MemoryStream ext = new MemoryStream();

                foreach (int extType in clientExtensions.Keys)
                {
                    WriteExtension(ext, extType, (byte[])clientExtensions[extType]);
                }

                TlsUtilities.WriteOpaque16(ext.ToArray(), outStr);
            }

            MemoryStream bos = new MemoryStream();
            TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
            TlsUtilities.WriteUint24((int)outStr.Length, bos);
            byte[] outBytes = outStr.ToArray();
            bos.Write(outBytes, 0, outBytes.Length);
            byte[] message = bos.ToArray();
            SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
            connection_state = CS_CLIENT_HELLO_SEND;

            /*
            * We will now read data, until we have completed the handshake.
            */
            while (connection_state != CS_DONE)
            {
                SafeReadData();
            }

            this.tlsStream = new TlsStream(this);
        }
Пример #11
0
        public virtual void Connect(TlsClient tlsClient)
        {
            if (tlsClient == null)
            {
                throw new ArgumentNullException("tlsClient");
            }
            if (this.tlsClient != null)
            {
                throw new InvalidOperationException("Connect can only be called once");
            }

            /*
             * Send Client hello
             *
             * First, generate some random data.
             */
            this.securityParameters = new SecurityParameters();
            this.securityParameters.clientRandom = new byte[32];
            random.NextBytes(securityParameters.clientRandom, 4, 28);
            TlsUtilities.WriteGmtUnixTime(securityParameters.clientRandom, 0);

            this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
            this.tlsClient        = tlsClient;
            this.tlsClient.Init(tlsClientContext);

            MemoryStream outStr = new MemoryStream();

            TlsUtilities.WriteVersion(outStr);
            outStr.Write(securityParameters.clientRandom, 0, 32);

            /*
             * Length of Session id
             */
            TlsUtilities.WriteUint8(0, outStr);

            this.offeredCipherSuites = this.tlsClient.GetCipherSuites();

            // ExtensionType -> byte[]
            this.clientExtensions = this.tlsClient.GetClientExtensions();

            // Cipher Suites (and SCSV)
            {
                /*
                 * RFC 5746 3.4.
                 * The client MUST include either an empty "renegotiation_info"
                 * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
                 * cipher suite value in the ClientHello.  Including both is NOT
                 * RECOMMENDED.
                 */
                bool noRenegExt = clientExtensions == null ||
                                  !clientExtensions.Contains(ExtensionType.renegotiation_info);

                int count = offeredCipherSuites.Length;
                if (noRenegExt)
                {
                    // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
                    ++count;
                }

                TlsUtilities.WriteUint16(2 * count, outStr);

                for (int i = 0; i < offeredCipherSuites.Length; ++i)
                {
                    TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
                }

                if (noRenegExt)
                {
                    TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
                }
            }

            /*
             * Compression methods, just the null method.
             */
            this.offeredCompressionMethods = tlsClient.GetCompressionMethods();

            {
                TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
                for (int i = 0; i < offeredCompressionMethods.Length; ++i)
                {
                    TlsUtilities.WriteUint8((byte)offeredCompressionMethods[i], outStr);
                }
            }

            // Extensions
            if (clientExtensions != null)
            {
                MemoryStream ext = new MemoryStream();

                foreach (ExtensionType extType in clientExtensions.Keys)
                {
                    WriteExtension(ext, extType, (byte[])clientExtensions[extType]);
                }

                TlsUtilities.WriteOpaque16(ext.ToArray(), outStr);
            }

            MemoryStream bos = new MemoryStream();

            TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
            TlsUtilities.WriteUint24((int)outStr.Length, bos);
            byte[] outBytes = outStr.ToArray();
            bos.Write(outBytes, 0, outBytes.Length);
            byte[] message = bos.ToArray();
            SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
            connection_state = CS_CLIENT_HELLO_SEND;

            /*
             * We will now read data, until we have completed the handshake.
             */
            while (connection_state != CS_DONE)
            {
                SafeReadData();
            }

            this.tlsStream = new TlsStream(this);
        }
Пример #12
0
        protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client)
        {
            ProtocolVersion client_version = client.ClientVersion;

            if (!client_version.IsDtls)
            {
                throw new TlsFatalAlert(AlertDescription.internal_error);
            }

            TlsClientContextImpl context = state.clientContext;

            context.SetClientVersion(client_version);

            SecurityParameters securityParameters = context.SecurityParameters;

            // Session ID
            byte[] session_id = TlsUtilities.EmptyBytes;
            if (state.tlsSession != null)
            {
                session_id = state.tlsSession.SessionID;
                if (session_id == null || session_id.Length > 32)
                {
                    session_id = TlsUtilities.EmptyBytes;
                }
            }

            bool fallback = client.IsFallback;

            state.offeredCipherSuites = client.GetCipherSuites();

            if (session_id.Length > 0 && state.sessionParameters != null)
            {
                if (!state.sessionParameters.IsExtendedMasterSecret ||
                    !Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite) ||
                    CompressionMethod.cls_null != state.sessionParameters.CompressionAlgorithm)
                {
                    session_id = TlsUtilities.EmptyBytes;
                }
            }

            state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions());

            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions);

            MemoryStream buf = new MemoryStream();

            TlsUtilities.WriteVersion(client_version, buf);

            buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);

            TlsUtilities.WriteOpaque8(session_id, buf);

            // Cookie
            TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf);

            // Cipher Suites (and SCSV)
            {
                /*
                 * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
                 * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
                 * ClientHello. Including both is NOT RECOMMENDED.
                 */
                byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info);
                bool   noRenegExt   = (null == renegExtData);

                bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);

                if (noRenegExt && noRenegSCSV)
                {
                    // TODO Consider whether to default to a client extension instead
                    state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
                }

                /*
                 * RFC 7507 4. If a client sends a ClientHello.client_version containing a lower value
                 * than the latest (highest-valued) version supported by the client, it SHOULD include
                 * the TLS_FALLBACK_SCSV cipher suite value in ClientHello.cipher_suites [..]. (The
                 * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends
                 * to negotiate.)
                 */
                if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
                {
                    state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
                }

                TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
            }

            TlsUtilities.WriteUint8ArrayWithUint8Length(new byte[] { CompressionMethod.cls_null }, buf);

            TlsProtocol.WriteExtensions(buf, state.clientExtensions);

            return(buf.ToArray());
        }