public Record[] ProcessHandshakeMessages(ProtocolVersion version,
                                                 HandshakeMessage[] messages,
                                                 int maxFragmentLength)
        {
            List<Record> records = new List<Record>();

            // Write all handshake data to memory stream
            MemoryStream memStream = new MemoryStream();
            for (int i = 0; i < messages.Length; i++)
            {
                byte[] msgBytes = messages[i].Encode();
                memStream.Write(msgBytes, 0, msgBytes.Length);
            }

            // Read handshake data from memory stream one at a time
            memStream.Position = 0;
            while (memStream.Position < memStream.Length)
            {
                long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength);
                byte[] fragment = new byte[fragmentLength];
                memStream.Read(fragment, 0, fragment.Length);
                Record record = new Record(RecordType.Handshake, version);
                record.Fragment = fragment;
                records.Add(record);
            }

            return records.ToArray();
        }
        public async Task SendAsync(Record[] records, CancellationToken token)
        {
            if (records == null)
            {
                throw new ArgumentNullException(nameof(records));
            }

            // Make sure fragments are not too long
            foreach (Record record in records)
            {
                if (record.Fragment.Length > MAX_RECORD_SIZE)
                {
                    throw new RecordTooLargeException("Trying to send a too large fragment: " + record.Fragment.Length);
                }

                Log.Debug(">> RECORD TX, Type {0}, Len {1}.", record.Type, record.FragmentLength);

                var buffer = record.GetBytes();

                try
                {
                    await this.innerStream.WriteAsync(buffer, 0, buffer.Length, token);
                    break;
                }
                catch (Exception ex)
                {
                    Log.Error("Send failed: {0}", ex.Message);
                    throw;
                }
            }
        }
        public Record[] ProcessOutputData(ProtocolVersion version, byte type, byte[] data, int maxFragmentLength)
        {
            List<Record> records = new List<Record>();

            // Read handshake data from memory stream one at a time
            MemoryStream memStream = new MemoryStream(data);
            while (memStream.Position < memStream.Length)
            {
                long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength);
                byte[] fragment = new byte[fragmentLength];
                memStream.Read(fragment, 0, fragment.Length);
                Record record = new Record((RecordType)type, version);
                record.Fragment = fragment;
                records.Add(record);
            }

            return records.ToArray();
        }
        public HandshakeMessage[] ProcessHandshakeRecord(ProtocolVersion version,
                                                         Record record)
        {
            List<HandshakeMessage> ret = new List<HandshakeMessage>();

            // Write record to input stream and attempt to parse a handshake message
            _inputStream.Write(record.Fragment, 0, record.Fragment.Length);
            while (_inputStream.Length - _inputtedBytes >= 4)
            {
                byte[] inputBuffer = _inputStream.GetBuffer();

                int dataLength = (inputBuffer[_inputtedBytes + 1] << 16) |
                                 (inputBuffer[_inputtedBytes + 2] << 8) |
                                  inputBuffer[_inputtedBytes + 3];

                // Check that if we have enough data to read message now
                if (4 + dataLength > _inputStream.Length - _inputtedBytes)
                    break;

                byte[] msgData = new byte[4 + dataLength];
                long writePosition = _inputStream.Position;

                // Read message data from inputStream
                _inputStream.Position = _inputtedBytes;
                _inputStream.Read(msgData, 0, msgData.Length);
                _inputStream.Position = writePosition;
                _inputtedBytes += msgData.Length;

                // Add resulting handshake message to the results
                ret.Add(HandshakeMessage.GetInstance(version, msgData));
            }

            if (_inputtedBytes == _inputStream.Length)
            {
                // Reset stream to save memory
                _inputStream.Position = 0;
                _inputStream.SetLength(0);
                _inputtedBytes = 0;
            }

            return ret.ToArray();
        }
 private void ProcessUnknownRecord(Record record)
 {
     throw new InvalidOperationException("Unknown record");
 }
        private async Task ProcessSendFatalAlert(Alert alert, CancellationToken token)
        {
            this.logger?.Warn("Processing fatal alert...");
            try
            {
                // Create and encrypt the alert record
                Record record = new Record(RecordType.Alert, this._handshakeSession.NegotiatedVersion, alert.GetBytes());
                this._recordHandler.ProcessOutputRecord(record);

                // Attempt sending the alert record
                await this._recordStream.SendAsync(new Record[] { record }, token);
            }
            catch (Exception ex)
            {
                this.logger?.Error("Processing fatal alert failed: {0}", ex.Message);
            }
            finally
            {
                if (this._recordStream != null)
                {
                    this.logger?.Info("Closing record stream...");
                    this._recordStream.Flush();
                    this._recordStream.Close();
                }
            }
        }
 private void ProcessAlertRecord(Record record)
 {
     // TODO: Received an alert, handle correctly
     Alert alert = new Alert(record.Fragment);
     this.logger?.Debug("Received an alert: " + alert.Description);
     if (alert.Level == AlertLevel.Fatal)
     {
         // Fatal alerts don't need close notify
         _recordStream.Close();
         throw new Exception("Received a fatal alert");
     }
 }
        private async Task SendServerChangeCipherSpec(CancellationToken token)
        {
            this.logger?.Debug("Sending change cipher spec to client...");

            // Create the change cipher spec protocol packet
            // NOTE: this has to be before recordHandler.ChangeLocalState since it uses the old state
            var record = new Record(
                                    RecordType.ChangeCipherSpec,
                                    this._handshakeSession.NegotiatedVersion,
                                    new byte[] { 0x01 });
            this._recordHandler.ProcessOutputRecord(record);

            // NOTE: keep this before recordHandler.ChangeLocalState since it may generate masterSecret
            this._handshakeSession.LocalChangeCipherSpec();

            // Change cipher suite in record handler and handle it in handshake session
            this._recordHandler.SetCipherSuite(this._handshakeSession.CipherSuite, this._handshakeSession.ConnectionState);
            this._recordHandler.ChangeLocalState();

            // Send the change cipher spec protocol packet 
            await this._recordStream.SendAsync(new[] { record }, token);
        }
        public async Task<Record[]> ReceiveAsync(CancellationToken token)
        {
            var records = new List<Record>();
            var readBuffer = new byte[MAX_RECORD_SIZE];
            var needsMoreData = true;

            while (!token.IsCancellationRequested)
            {
                if (needsMoreData)
                {
                    // hook to combine token.cancel with stream.close, otherwise ReadAsync never returns
                    using (token.Register(this.innerStream.Close))
                    {
                        try
                        {
                            var readBytes = await this.innerStream.ReadAsync(readBuffer, 0, readBuffer.Length, token);
                            if (readBytes < 1)
                            {
                                continue;
                            }
                            this.rcvBuffer.AddRange(readBuffer.Take(readBytes));
                        }
                        catch (Exception e)
                        {
                            if (token.IsCancellationRequested)
                            {
                                throw new OperationCanceledException("Read was cancelled");
                            }

                            Log.Error("Read failed: ", e.Message);
                        }
                    }                   
                }

                // do not inspect buffer if there is not even a packet header
                if (this.rcvBuffer.Count < 5)
                {
                    await Task.Delay(5, token);
                    continue;
                }

                
                // We require the fragment bytes
                int fragmentLength = (this.rcvBuffer[3] << 8) | this.rcvBuffer[4];
                if (fragmentLength > this.rcvBuffer.Count + 5)
                {
                    // read more data
                    needsMoreData = true;
                    continue;
                }

                needsMoreData = false;
                // Construct the TLSRecord returned as result
                var recordBuffer = this.rcvBuffer.Take(5 + fragmentLength).ToArray();
                var record = new Record(recordBuffer);
                Log.Debug("<< RECORD RX, Type {0}, Fragment Len {1} Seq {2}.", record.Type, record.FragmentLength, record.SequenceNumber);

                Buffer.BlockCopy(recordBuffer, 5, record.Fragment, 0, fragmentLength);
                records.Add(record);

                // remove record from input buffer
                this.rcvBuffer.RemoveRange(0, 5 + fragmentLength);

                if (this.rcvBuffer.Count == 0)
                {
                    // return records
                    break;
                }
            }

            return records.ToArray();
        }