public override Message ProcessIncoming(MessageHeader header, byte[] chiper, ref byte[] plaintext)
        {
            var messageDirection = Message.GetMessageDirection(header.ID);
            // Message instance that we will return to the caller.
            var message = MessageFactory.Create(header.ID);

            // Unencrypted byte array.
            plaintext = null;
            lock (_lock)
            {
                // Handshaking.
                if (_state <= 1)
                {
                    // Handshakes are sent unencrypted.
                    // First message by both ends is always sent unencrypted.
                    plaintext = (byte[])chiper.Clone();

                    // Use the first message direction to configure the processor.
                    if (_state == 0)
                    {
                        _clientCrypto = new Crypto8(GetOppositeDirection(messageDirection), _keyPair);
                    }
                    if (_state == 1)
                    {
                        _serverCrypto = new Crypto8(GetOppositeDirection(messageDirection), Crypto8.GenerateKeyPair());
                    }
                }

                if (_state == 4)
                {
                    _clientCrypto.UpdateNonce((byte[])_serverNonce.Clone(), UpdateNonceType.Encrypt);
                    _clientCrypto.UpdateNonce((byte[])_clientNonce.Clone(), UpdateNonceType.Decrypt);
                    _clientCrypto.UpdateSharedKey(_serverCrypto.SharedKey);
                }

                // _state == 1 means we are the server.
                // Usually processing 10101 - LoginRequestMessage.
                if (_state == 2)
                {
                    _serverCrypto.UpdateSharedKey(Crypto8.SupercellPublicKey);

                    // -> Pre-Encryption.
                    // Copies the public key appended to the beginning of the message.
                    var publicKey = new byte[CoCKeyPair.KeyLength];
                    Buffer.BlockCopy(chiper, 0, publicKey, 0, CoCKeyPair.KeyLength);

                    Debug.WriteLine($"Public-Key from {header.ID}: {ToHexString(publicKey)}");

                    // Copies the remaining bytes into the plaintext buffer
                    var plaintextLen = header.Length - CoCKeyPair.KeyLength;
                    plaintext = new byte[plaintextLen];
                    Buffer.BlockCopy(chiper, CoCKeyPair.KeyLength, plaintext, 0, plaintextLen);

                    // Crypto8 will take publicKey & _keyPair.PublicKey and generate a blake2b nonce
                    _clientCrypto.UpdateSharedKey(publicKey);
                    // Then use _keyPair.PrivateKey, publicKey and nonce to decrypt.
                    _clientCrypto.Decrypt(ref plaintext);

                    // -> Post-Encryption.
                    _sessionKey  = new byte[CoCKeyPair.NonceLength];
                    _clientNonce = new byte[CoCKeyPair.NonceLength];

                    // Copy the SessionKey and the ClientNonce.
                    Buffer.BlockCopy(plaintext, 0, _sessionKey, 0, CoCKeyPair.NonceLength);
                    Buffer.BlockCopy(plaintext, CoCKeyPair.NonceLength, _clientNonce, 0, CoCKeyPair.NonceLength);

                    Debug.WriteLine($"Session-key from {header.ID}: {ToHexString(_sessionKey)}");
                    Debug.WriteLine($"Client-nonce from {header.ID}: {ToHexString(_clientNonce)}");

                    var actualMessage = new byte[plaintext.Length - (CoCKeyPair.NonceLength * 2)];
                    Buffer.BlockCopy(plaintext, CoCKeyPair.NonceLength * 2, actualMessage, 0, actualMessage.Length);

                    plaintext = actualMessage;
                }
                else if (_state == 3)
                {
                    _clientCrypto.UpdateNonce(_clientNonce, UpdateNonceType.Blake);
                    _serverCrypto.UpdateNonce(_clientNonce, UpdateNonceType.Blake);
                    _serverCrypto.Decrypt(ref chiper);

                    // Post-Encryption
                    // Copies the public key appended to the beginning of the message.
                    _serverNonce = new byte[CoCKeyPair.NonceLength];
                    Buffer.BlockCopy(chiper, 0, _serverNonce, 0, CoCKeyPair.NonceLength);

                    var publicKey = new byte[CoCKeyPair.KeyLength];
                    Buffer.BlockCopy(chiper, CoCKeyPair.NonceLength, publicKey, 0, CoCKeyPair.KeyLength);

                    _serverCrypto.UpdateNonce((byte[])_serverNonce.Clone(), UpdateNonceType.Decrypt);
                    _serverCrypto.UpdateNonce((byte[])_clientNonce.Clone(), UpdateNonceType.Encrypt);
                    _serverCrypto.UpdateSharedKey(publicKey);

                    Debug.WriteLine($"Server-Nonce from {header.ID}: {ToHexString(_serverNonce)}");
                    Debug.WriteLine($"New Public-Key from {header.ID}: {ToHexString(publicKey)}");

                    // Copies the remaining bytes into the plaintext buffer.
                    var plaintextLen = chiper.Length - CoCKeyPair.KeyLength - CoCKeyPair.NonceLength;
                    plaintext = new byte[plaintextLen];

                    Buffer.BlockCopy(chiper, CoCKeyPair.KeyLength + CoCKeyPair.NonceLength, plaintext, 0, plaintextLen);
                }
                else if (_state > 3)
                {
                    if (messageDirection == MessageDirection.Client)
                    {
                        _serverCrypto.Decrypt(ref chiper);
                    }

                    if (messageDirection == MessageDirection.Server)
                    {
                        _clientCrypto.Decrypt(ref chiper);
                    }

                    plaintext = chiper;
                }

                _state++;
            }
            try
            {
                using (var reader = new MessageReader(new MemoryStream(plaintext)))
                    message.ReadMessage(reader);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Ex: " + ex);
            }
            return(message);
        }
        private byte[] ProcessIncomingData(MessageDirection direction, MessageHeader header, BufferStream stream)
        {
            var plaintext = (byte[])null;
            var chiper    = new byte[header.Length];

            stream.Read(chiper, 0, header.Length);

            // Handshaking.
            if (_incommingState == 0)
            {
                // Handshakes are sent unencrypted.
                // First message by both ends is always sent unencrypted.
                plaintext = new byte[header.Length];
                Buffer.BlockCopy(chiper, 0, plaintext, 0, header.Length);

                // Use the first message direction to configure the processor.
                // If message is coming in, then the going out is opposite direction.
                _direction = GetOppositeDirection(direction);
                // If we're the client then we must have a serverkey.
                if (_direction == MessageDirection.Server && _serverKey == null)
                {
                    throw new InvalidOperationException("Public key of server was not specified.");
                }

                if (_crypto == null)
                {
                    _crypto = new Crypto8(_direction, _keyPair);
                }

                Debug.WriteLine($"Initialized Crypto8 {_direction} with private-key: {ToHexString(_keyPair.PrivateKey)}, public-key {_keyPair.PublicKey}");
            }

            // If somehow the incoming message to process comes from the same direction.
            // The MessageProcessor can only process message going in One Direction. ;]
            if (GetOppositeDirection(direction) != _direction)
            {
                throw new InvalidOperationException("Tried to process an incoming data from a message coming from the same direction."); // -> Protocol Exception?
            }
            // _incomingState == 2 means we are the server.
            // Usually processing 10101 - LoginRequestMessage.
            if (_incommingState == 2)
            {
                // -> Post-Encryption.
                // Copies the public key appended to the beginning of the message
                // sent of message 20100.
                var publicKey = new byte[KeyPair.KeyLength];
                Buffer.BlockCopy(chiper, 0, publicKey, 0, KeyPair.KeyLength);

                Debug.WriteLine($"Public-Key from {header.Id}: {ToHexString(publicKey)}");

                // Copies the remaining bytes into the postChiper buffer which
                // will be decrypted by _crypto.
                var tmpPlaintextLen = header.Length - KeyPair.KeyLength;
                var tmpPlaintext    = new byte[tmpPlaintextLen];
                Buffer.BlockCopy(chiper, KeyPair.KeyLength, tmpPlaintext, 0, tmpPlaintextLen);

                // Crypto8 will take the client's publicKey & _keyPair.PublicKey and generate a blake2b nonce
                _crypto.UpdateSharedKey(publicKey);
                // Then use _keyPair.PrivateKey, publicKey and nonce to decrypt.
                _crypto.Decrypt(ref tmpPlaintext);

                // -> Pre-Encryption.
                var sessionKey  = new byte[KeyPair.NonceLength];
                var remoteNonce = new byte[KeyPair.NonceLength];

                // Copy the SessionKey and the ClientNonce.
                Buffer.BlockCopy(tmpPlaintext, 0, sessionKey, 0, KeyPair.NonceLength);
                Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength, remoteNonce, 0, KeyPair.NonceLength);

                Debug.WriteLine($"Session-key from {header.Id}: {ToHexString(sessionKey)}");
                Debug.WriteLine($"Client-nonce from {header.Id}: {ToHexString(remoteNonce)}");

                // Copies the plaintext without the session key and the remote/client nonce.
                var plaintextLen = tmpPlaintext.Length - (KeyPair.NonceLength * 2);
                plaintext = new byte[plaintextLen];
                Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength * 2, plaintext, 0, plaintextLen);

                _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Decrypt);
                _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Blake);
                _sessionKey  = sessionKey;
                _remoteNonce = remoteNonce;

                _nstate = States.Authentifying;
            }
            // _incomingState == 3 means we are the client.
            // Usually processing 20104 - LoginSuccessMessage.
            else if (_incommingState == 3)
            {
                // -> Might want to generate random nonce here for the
                // client nonce.
                Debug.Assert(_localNonce != null);

                var tmpPlaintext = (byte[])chiper.Clone();

                // _crypto will use this nonce after the second key is passed to it
                // using _crypto.UpdateSharedKey.
                var localNonce = _localNonce;
                _crypto.UpdateNonce(localNonce, UpdateNonceType.Encrypt);
                _crypto.UpdateNonce(localNonce, UpdateNonceType.Blake);
                _crypto.Decrypt(ref tmpPlaintext);

                // -> Post-Encryption
                var remoteNonce = new byte[KeyPair.NonceLength];
                var key         = new byte[KeyPair.KeyLength];

                // Copies the ServerNonce and the new second secret key.
                Buffer.BlockCopy(tmpPlaintext, 0, remoteNonce, 0, KeyPair.NonceLength);
                Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength, key, 0, KeyPair.KeyLength);

                Debug.WriteLine($"Shared-key from {header.Id}: {ToHexString(key)}");
                Debug.WriteLine($"Server-nonce from {header.Id}: {ToHexString(remoteNonce)}");

                // Copies the plaintext without the server nonce and the new second secret key.
                var plaintextLen = tmpPlaintext.Length - KeyPair.NonceLength - KeyPair.KeyLength;
                plaintext = new byte[plaintextLen];
                Buffer.BlockCopy(tmpPlaintext, KeyPair.NonceLength + KeyPair.KeyLength, plaintext, 0, plaintextLen);

                _crypto.UpdateNonce(remoteNonce, UpdateNonceType.Decrypt);
                _crypto.UpdateSharedKey(key);

                _remoteNonce = remoteNonce;
                _key         = key;

                _nstate = States.Authentified;
            }
            // Messages after the previous states are processed the same way.
            else if (_incommingState > (int)_direction)
            {
                _nstate = States.Completed;

                plaintext = (byte[])chiper.Clone();
                _crypto.Decrypt(ref plaintext);
            }

            return(plaintext);
        }