Beispiel #1
0
        private bool StartEncrypt()
        {
            CryptoHandler crypto = new CryptoHandler();

            List <byte> encryptionRequest = new List <byte>();
            string      serverID          = "";

            if (protocol.protocolVersion < MCVersion.MC172Version)
            {
                serverID = "lilium-pre";
            }
            encryptionRequest.AddRange(getString(serverID));
            encryptionRequest.AddRange(getArray(crypto.getPublic()));
            byte[] token = new byte[4];
            var    rng   = new System.Security.Cryptography.RNGCryptoServiceProvider(); rng.GetBytes(token);

            encryptionRequest.AddRange(getArray(token));
            SendPacket(0x01, encryptionRequest);

            List <byte> encryptResponse = new List <byte>(readDataRAW(readNextVarIntRAW()));

            if (readNextVarInt(encryptResponse) == 0x01)
            {
                List <byte> dec = new List <byte>();
                dec.AddRange(crypto.Decrypt(readNextByteArray(encryptResponse)));
                dec.RemoveRange(0, dec.Count - 16);
                byte[] key_dec   = dec.ToArray();
                byte[] token_dec = token;

                EncStream      = CryptoHandler.getAesStream(Client.GetStream(), key_dec);
                this.encrypted = true;
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Read a specified clipboard data, encrypt and save it into a file.
        /// </summary>
        /// <param name="clipboardReader">The <see cref="ClipboardReader"/> used to read the data.</param>
        /// <param name="identifier">The data identifier.</param>
        /// <param name="clipboardDataPath">The full path to the clipboard data folder.</param>
        private void WriteClipboardDataToFile(ClipboardReader clipboardReader, DataIdentifier identifier, string clipboardDataPath)
        {
            Requires.NotNull(clipboardReader, nameof(clipboardReader));
            Requires.NotNull(identifier, nameof(identifier));
            Requires.NotNullOrWhiteSpace(clipboardDataPath, nameof(clipboardDataPath));

            var dataFilePath = Path.Combine(clipboardDataPath, $"{identifier.Identifier}.dat");

            if (File.Exists(dataFilePath))
            {
                Logger.Instance.Fatal(new FileLoadException($"The file {dataFilePath} already exists."));
            }

            var dataPassword = SecurityHelper.ToSecureString(SecurityHelper.EncryptString(SecurityHelper.ToSecureString(identifier.Identifier.ToString())));

            Requires.NotNull(dataPassword, nameof(dataPassword));

            clipboardReader.BeginRead(identifier.FormatName);
            Requires.IsTrue(clipboardReader.IsReadable);
            Requires.IsTrue(clipboardReader.CanReadNextBlock());

            using (var fileStream = File.OpenWrite(dataFilePath))
                using (var aesStream = new AesStream(fileStream, dataPassword, SecurityHelper.GetSaltKeys(dataPassword).GetBytes(16)))
                {
                    aesStream.AutoDisposeBaseStream = false;
                    while (clipboardReader.CanReadNextBlock())
                    {
                        var buffer = clipboardReader.ReadNextBlock();
                        aesStream.Write(buffer, 0, buffer.Length);
                    }
                    aesStream.Position = 0;
                }

            clipboardReader.EndRead();
        }
Beispiel #3
0
        public void Save(Stream stream)
        {
            EndianReader writer = new EndianReader(stream, Endianness.BigEndian);

            Bk.Save(stream);
            writer.PadToMultiple(0x40);

            TMD.Save(stream);
            writer.PadToMultiple(0x40);

            if (RawData != null)
            {
                RawData.Position = 0;
                Util.StreamCopy(stream, RawData);
            }
            else if (Content != null)
            {
                byte[] iv = new byte[0x10];
                BigEndianConverter.GetBytes(Content.Index).CopyTo(iv, 0);
                AesStream astream = new AesStream(stream, Key, iv);
                Data.Position = 0;
                Util.StreamCopy(astream, Data);
                astream.Close();
            }

            writer.PadToMultiple(0x40);
        }
        /// <summary>
        /// Upload the specified data file to the cloud storage service.
        /// </summary>
        /// <param name="clipboardDataPath">The full path to the clipboard data folder.</param>
        /// <param name="cloudFolder">The <see cref="CloudFolder"/> that contains informations about the application folder.</param>
        /// <param name="cloudDataPassword">The supposed password that we will use to encrypt the data before uploading it to the cloud.</param>
        /// <param name="dataIdentifier">The data identifier used to determines which file will be uploaded.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task UploadDataFileAsync(string clipboardDataPath, CloudFolder cloudFolder, SecureString cloudDataPassword, DataIdentifier dataIdentifier)
        {
            var dataService                = ServiceLocator.GetService <DataService>();
            var fileName                   = $"{dataIdentifier.Identifier}.dat";
            var cloudDataFilePath          = Path.Combine(cloudFolder.FullPath, fileName);
            var localDataFilePath          = Path.Combine(clipboardDataPath, fileName);
            var temporaryLocalDataFilePath = Path.Combine(clipboardDataPath, $"{fileName}.temp");
            var localDataPassword          = SecurityHelper.ToSecureString(SecurityHelper.EncryptString(SecurityHelper.ToSecureString(dataIdentifier.Identifier.ToString())));

            Requires.NotNull(localDataPassword, nameof(localDataPassword));

            if (!File.Exists(localDataFilePath))
            {
                Logger.Instance.Warning($"Data file {dataIdentifier.Identifier} not found locally, it has probably been removed while synchronizing.");
                return;
            }

            using (var fileStream = File.OpenRead(localDataFilePath))
                using (var temporaryLocalFileStream = File.Open(temporaryLocalDataFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    using (var localAesStream = new AesStream(fileStream, localDataPassword, SecurityHelper.GetSaltKeys(localDataPassword).GetBytes(16)))
                        using (var cloudAesStream = new AesStream(temporaryLocalFileStream, cloudDataPassword, SecurityHelper.GetSaltKeys(cloudDataPassword).GetBytes(16)))
                        {
                            dataService.CopyData(localAesStream, cloudAesStream);
                            await CurrentCloudStorageProvider.UploadFileAsync(temporaryLocalFileStream, cloudDataFilePath);
                        }

            if (File.Exists(temporaryLocalDataFilePath))
            {
                File.Delete(temporaryLocalDataFilePath);
            }
        }
Beispiel #5
0
        private async Task <MemoryStream> ReadFileFromServerAsync(string filePath)
        {
            var cloudStorageProviderMock = GetCloudStorageProviderMock();

            if (File.Exists(filePath))
            {
                var password = await GetCloudDataPasswordAsync();

                var result = new MemoryStream();
                using (var temporaryLocalFileStream = new MemoryStream())
                {
                    await cloudStorageProviderMock.DownloadFileAsync(filePath, temporaryLocalFileStream);

                    temporaryLocalFileStream.Position = 0;
                    using (var cloudAesStream = new AesStream(temporaryLocalFileStream, password, SecurityHelper.GetSaltKeys(password).GetBytes(16)))
                    {
                        CopyStream(cloudAesStream, result);
                    }
                }

                return(result);
            }

            return(null);
        }
Beispiel #6
0
        /// <summary>
        /// Write a specified clipboard data from an encrypted file.
        /// </summary>
        /// <param name="clipboardWriter">The <see cref="ClipboardWriter"/> used to write the data.</param>
        /// <param name="identifier">The data identifier.</param>
        /// <param name="clipboardDataPath">The full path to the clipboard data folder.</param>
        private void ReadFileToClipboardData(ClipboardWriter clipboardWriter, DataIdentifier identifier, string
                                             clipboardDataPath)
        {
            Requires.NotNull(clipboardWriter, nameof(clipboardWriter));
            Requires.NotNull(identifier, nameof(identifier));
            Requires.NotNullOrWhiteSpace(clipboardDataPath, nameof(clipboardDataPath));
            var dataFilePath = Path.Combine(clipboardDataPath, $"{identifier.Identifier}.dat");

            if (!File.Exists(dataFilePath))
            {
                return;
            }

            var dataPassword =
                SecurityHelper.ToSecureString(SecurityHelper.EncryptString(SecurityHelper.ToSecureString(
                                                                               identifier.Identifier.ToString(
                                                                                   ))));

            Requires.NotNull(dataPassword, nameof(dataPassword));
            var fileStream = File.OpenRead(dataFilePath);
            var aesStream  = new AesStream(fileStream, dataPassword,
                                           SecurityHelper.GetSaltKeys(dataPassword).GetBytes(16));

            clipboardWriter.AddData(identifier.FormatName, aesStream, fileStream);
        }
Beispiel #7
0
        public void Encrypt_Decrypt_Stream()
        {
            using (var media = new MemoryStream())
                using (var crypto = new AesStream("abc", media))
                {
                    var input0 = new byte[8192];
                    var input1 = new byte[8192];
                    var input2 = new byte[8192];

                    var output0 = new byte[8192];
                    var output1 = new byte[8192];
                    var output2 = new byte[8192];

                    input0.Fill(100, 0, 8192);
                    input1.Fill(101, 0, 8192);
                    input2.Fill(102, 0, 8192);

                    // write 0, 2, 1 but in order 0, 1, 2
                    crypto.Position = 0 * 8192;
                    crypto.Write(input0, 0, 8192);

                    crypto.Position = 2 * 8192;
                    crypto.Write(input2, 0, 8192);

                    crypto.Position = 1 * 8192;
                    crypto.Write(input1, 0, 8192);

                    // read encrypted data
                    media.Position = 0;
                    media.Read(output0, 0, 8192);
                    media.Read(output1, 0, 8192);
                    media.Read(output2, 0, 8192);

                    Assert.IsFalse(output0.All(x => x == 100));
                    Assert.IsFalse(output1.All(x => x == 101));
                    Assert.IsFalse(output2.All(x => x == 102));

                    // read decrypted data
                    crypto.Position = 0 * 8192;
                    crypto.Read(output0, 0, 8192);

                    crypto.Position = 2 * 8192;
                    crypto.Read(output2, 0, 8192);

                    crypto.Position = 1 * 8192;
                    crypto.Read(output1, 0, 8192);

                    Assert.IsTrue(output0.All(x => x == 100));
                    Assert.IsTrue(output1.All(x => x == 101));
                    Assert.IsTrue(output2.All(x => x == 102));
                }
        }
        public bool SwitchToEncrypted(string serverID, byte[] Serverkey, byte[] token)
        {
            if (ServerData.OnlineMode)
            {
                var    crypto    = CryptoHandler.DecodeRSAPublicKey(Serverkey);
                byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
                byte[] key_enc   = crypto.Encrypt(secretKey, false);
                byte[] token_enc = crypto.Encrypt(token, false);
                //Console.WriteLine(key_enc.Length + " " + token_enc.Length);

                SendPacket(0x01, concatBytes(getArray(key_enc), getArray(token_enc)));

                this.s    = CryptoHandler.getAesStream(c.GetStream(), secretKey);
                encrypted = true;

                int         packetID   = -1;
                List <byte> packetData = new List <byte>();
                while (true)
                {
                    readNextPacket(ref packetID, packetData);
                    if (packetID == 0x00)
                    {
                        handler.OnConnectionLost(Conn.DisconnectReason.LoginRejected, readNextString(packetData));
                        return(false);
                    }
                    else if (packetID == 0x02)//Logined
                    {
                        Debug.Log("Login Success");
                        login_phase = false;
                        handler.OnLogin(packetData);
                        StartUpdating();
                        return(true);
                    }
                    else
                    {
                        if (packetID == 0x03 && login_phase)
                        {
                            if (protocolversion >= MCVersion.MC18Version)
                            {
                                compression_treshold = readNextVarInt(packetData);
                            }
                        }
                        handler.receivePacket(packetID, packetData);
                    }
                }
            }
            else
            {
                handler.OnConnectionLost(Conn.DisconnectReason.LoginRejected, ServerData.MsgEncryptReject);
            }
            return(false);
        }
Beispiel #9
0
        public void AesStreamRead()
        {
            var cryptedData = new byte[] { 238, 75, 117, 248, 55 };
            var password    = SecurityHelper.ToSecureString("MyPassword");

            using (var baseStream = new MemoryStream(cryptedData))
                using (var aesStream = new AesStream(baseStream, password, SecurityHelper.GetSaltKeys(password).GetBytes(16)))
                {
                    var data = new byte[aesStream.Length];
                    aesStream.Read(data, 0, data.Length);

                    Assert.IsTrue(data.ToArray().SequenceEqual(new byte[] { 72, 101, 108, 108, 111 }));
                    Assert.AreEqual(Encoding.ASCII.GetString(data), "Hello");
                }
        }
Beispiel #10
0
        public void TestAesStream()
        {
            var baseStream = new MemoryStream();
            var key = new byte[]
                { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            var encryptStream = new AesStream(baseStream, key);
            var plaintext = Encoding.UTF8.GetBytes("Hello, world!");
            encryptStream.Write(plaintext, 0, plaintext.Length);

            baseStream.Seek(0, SeekOrigin.Begin);
            var decryptStream = new AesStream(baseStream, key);
            byte[] buffer = new byte[plaintext.Length];
            decryptStream.Read(buffer, 0, buffer.Length);

            Assert.AreEqual("Hello, world!", Encoding.UTF8.GetString(buffer));
        }
Beispiel #11
0
        public void TestAesStream()
        {
            var baseStream = new MemoryStream();
            var key        = new byte[]
            { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            var encryptStream = new AesStream(baseStream, key);
            var plaintext     = Encoding.UTF8.GetBytes("Hello, world!");

            encryptStream.Write(plaintext, 0, plaintext.Length);

            baseStream.Seek(0, SeekOrigin.Begin);
            var decryptStream = new AesStream(baseStream, key);

            byte[] buffer = new byte[plaintext.Length];
            decryptStream.Read(buffer, 0, buffer.Length);

            Assert.AreEqual("Hello, world!", Encoding.UTF8.GetString(buffer));
        }
Beispiel #12
0
        public async Task TestEncryption(byte[] testData)
        {
            var random    = new Random();
            var sharedKey = new byte[32];

            random.NextBytes(sharedKey);

            await using var memoryStream = new MemoryStream();
            await using var stream       = new AesStream(memoryStream, sharedKey);

            await stream.WriteAsync(testData);

            memoryStream.Position = 0;

            var incomingRandomData = new byte[testDataLength];
            await stream.ReadAsync(incomingRandomData, 0, incomingRandomData.Length);

            Assert.Equal(testData, incomingRandomData);
        }
Beispiel #13
0
        private async Task <List <CloudDataEntry> > GetDataEntriesFromServerAsync()
        {
            var cloudStorageProviderMock = GetCloudStorageProviderMock();

            if (Directory.Exists(cloudStorageProviderMock.TemporaryFolder))
            {
                using (var memoryStream = new MemoryStream())
                {
                    await cloudStorageProviderMock.DownloadFileAsync(cloudStorageProviderMock.TemporaryFolder + @"\.clipboard", memoryStream);

                    var cloudDataPassword = await GetCloudDataPasswordAsync();

                    using (var aesStream = new AesStream(memoryStream, cloudDataPassword, SecurityHelper.GetSaltKeys(cloudDataPassword).GetBytes(16)))
                    {
                        var data = new byte[aesStream.Length];
                        aesStream.Read(data, 0, data.Length);
                        return(JsonConvert.DeserializeObject <List <CloudDataEntry> >(Encoding.UTF8.GetString(data)));
                    }
                }
            }

            return(null);
        }
Beispiel #14
0
        /// <summary>
        /// Force to synchronize that data with the cloud.
        /// </summary>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        internal async Task SynchronizeAsync()
        {
            if (IsSynchronizing)
            {
                return;
            }

            Logger.Instance.Information("Synchronization with the cloud started.");
            IsSynchronizing = true;

            if (CurrentCloudStorageProvider == null)
            {
                Logger.Instance.Warning("The user is not logged to any cloud storage provider. The synchronization stopped.");
                IsSynchronizing = false;
                return;
            }

            if (!CoreHelper.IsUnitTesting() && !SystemInfoHelper.CheckForInternetConnection())
            {
                Logger.Instance.Warning("There is no internet connection. The synchronization stopped.");
                IsSynchronizing = false;
                return;
            }

            SynchronizationStarted?.Invoke(this, new EventArgs());

            try
            {
                if (!await CurrentCloudStorageProvider.TryAuthenticateAsync())
                {
                    Logger.Instance.Warning("The user is not authenticated correctly. Consider unlink the app and connect again. The synchronization stopped.");
                    IsSynchronizing = false;
                    SynchronizationEnded?.Invoke(this, new EventArgs());
                    return;
                }

                var userId = SecurityHelper.ToSecureString(await CurrentCloudStorageProvider.GetUserIdAsync());

                if (string.IsNullOrWhiteSpace(SecurityHelper.ToUnsecureString(userId)))
                {
                    Logger.Instance.Warning("The user's id from the cloud storage provider has not been found. The synchronization stopped.");
                    IsSynchronizing = false;
                    SynchronizationEnded?.Invoke(this, new EventArgs());
                    SynchronizationFailed?.Invoke(this, new EventArgs());
                    return;
                }

                Logger.Instance.Information("Freezing the data before synchronize.");
                var dataService = ServiceLocator.GetService <DataService>();
                var cloudDataEntryFromServer = new List <CloudDataEntry>();
                var cloudAppFolder           = await CurrentCloudStorageProvider.GetAppFolderAsync();

                var cloudDataEntryFilePath = Path.Combine(cloudAppFolder.FullPath, Consts.DataEntryFileName);
                var cloudDataPassword      = SecurityHelper.ToSecureString(SecurityHelper.EncryptString(userId, SecurityHelper.ToSecureString(await CurrentCloudStorageProvider.GetUserNameAsync())));
                var localFrozenDataEntries = DataHelper.FromByteArray <AsyncObservableCollection <DataEntry> >(DataHelper.ToByteArray(dataService.DataEntries));
                var localFrozenCache       = DataHelper.FromByteArray <List <DataEntryCache> >(DataHelper.ToByteArray(dataService.Cache));

                // Download data from server.
                if (cloudAppFolder.Files.Any(file => file.FullPath == cloudDataEntryFilePath))
                {
                    Logger.Instance.Information("Downloading the data entry file from the server.");
                    try
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            await CurrentCloudStorageProvider.DownloadFileAsync(cloudDataEntryFilePath, memoryStream);

                            memoryStream.Position = 0;
                            using (var aesStream = new AesStream(memoryStream, cloudDataPassword, SecurityHelper.GetSaltKeys(cloudDataPassword).GetBytes(16)))
                            {
                                var data = new byte[aesStream.Length];
                                aesStream.Read(data, 0, data.Length);
                                cloudDataEntryFromServer = JsonConvert.DeserializeObject <List <CloudDataEntry> >(Encoding.UTF8.GetString(data));
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        Logger.Instance.Warning($"Unable to download or read the data file entry from the cloud for the following reason : {exception.Message}");
                        IsSynchronizing = false;
                        SynchronizationEnded?.Invoke(this, new EventArgs());
                        SynchronizationFailed?.Invoke(this, new EventArgs());
                        return;
                    }
                }
                else
                {
                    Logger.Instance.Information("There is no data entry file on the server yet.");
                }

                // Synchronize locally the data. The result must corresponds to what we will have locally and on the server at the end of the synchronization process.
                var cloudDataEntryToServer = dataService.DifferenceLocalAndCloudDataEntries(cloudDataEntryFromServer);

                // Download the needed data from the server to the local machine.
                Logger.Instance.Information("Downloading the needed data from the server to the local machine.");
                var dataToDownload = cloudDataEntryFromServer.Cast <DataEntryBase>().Except(localFrozenDataEntries, (item1, item2) => item1.Identifier == item2.Identifier).ToList();
                var taskList       = new List <Task>();
                foreach (var cloudDataEntry in dataToDownload)
                {
                    if (dataToDownload.Any(data => localFrozenCache.Any(item => data.Identifier == item.Identifier && item.Status == DataEntryStatus.Deleted)))
                    {
                        continue;
                    }

                    foreach (var dataEntryDataIdentifier in cloudDataEntry.DataIdentifiers)
                    {
                        var task = DownloadDataFileAsync(dataService.ClipboardDataPath, cloudAppFolder, cloudDataPassword, dataEntryDataIdentifier);
                        taskList.Add(task);
                    }
                }
                await Task.WhenAll(taskList);

                // Delete the needed data from the server
                Logger.Instance.Information("Deleting the needed data from the server.");
                taskList = new List <Task>();
                foreach (var dataServiceDataEntry in localFrozenDataEntries.Where(item => !item.CanSynchronize))
                {
                    foreach (var dataEntryDataIdentifier in dataServiceDataEntry.DataIdentifiers)
                    {
                        var task = DeleteFileAsync(cloudAppFolder, dataEntryDataIdentifier);
                        taskList.Add(task);
                    }
                }
                await Task.WhenAll(taskList);

                taskList = new List <Task>();
                foreach (var cacheEntry in localFrozenCache.Where(item => item.Status == DataEntryStatus.Deleted))
                {
                    var dataEntry = cloudDataEntryFromServer.SingleOrDefault(item => item.Identifier == cacheEntry.Identifier);
                    if (dataEntry != null)
                    {
                        foreach (var dataEntryDataIdentifier in dataEntry.DataIdentifiers)
                        {
                            var task = DeleteFileAsync(cloudAppFolder, dataEntryDataIdentifier);
                            taskList.Add(task);
                        }
                    }
                }
                await Task.WhenAll(taskList);

                await dataService.MakeCacheSynchronized(cloudDataEntryToServer, true, localFrozenCache);

                localFrozenDataEntries = DataHelper.FromByteArray <AsyncObservableCollection <DataEntry> >(DataHelper.ToByteArray(dataService.DataEntries));
                localFrozenCache       = DataHelper.FromByteArray <List <DataEntryCache> >(DataHelper.ToByteArray(dataService.Cache));

                // Upload the needed data from the server to the local machine
                Logger.Instance.Information("Uploading the needed data from the server to the local machine.");
                var dataToUpload = localFrozenDataEntries.Cast <DataEntryBase>().Except(cloudDataEntryFromServer, (item1, item2) => item1.Identifier == item2.Identifier);
                taskList = new List <Task>();
                foreach (var dataEntry in dataToUpload)
                {
                    var localDataEntry = localFrozenDataEntries.Single(item => item.Identifier == dataEntry.Identifier);
                    if (!localDataEntry.CanSynchronize || localDataEntry.Thumbnail.Type == ThumbnailDataType.Files)
                    {
                        continue;
                    }

                    foreach (var dataEntryDataIdentifier in dataEntry.DataIdentifiers)
                    {
                        var task = UploadDataFileAsync(dataService.ClipboardDataPath, cloudAppFolder, cloudDataPassword, dataEntryDataIdentifier);
                        taskList.Add(task);
                    }
                }
                await Task.WhenAll(taskList);

                // Upload the new data to the server.
                Logger.Instance.Information("Uploading the data entry file to the server.");

                using (var memoryStream = new MemoryStream())
                    using (var aesStream = new AesStream(memoryStream, cloudDataPassword, SecurityHelper.GetSaltKeys(cloudDataPassword).GetBytes(16)))
                    {
                        var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cloudDataEntryToServer));
                        aesStream.Write(data, 0, data.Length);
                        aesStream.Position = 0;
                        await CurrentCloudStorageProvider.UploadFileAsync(memoryStream, cloudDataEntryFilePath);
                    }

                await dataService.MakeCacheSynchronized(cloudDataEntryToServer, false, localFrozenCache);
            }
            catch (Exception exception)
            {
                Logger.Instance.Warning($"Unable to synchronize for the following reason : {exception.Message}. {exception.InnerException?.Message}");
                SynchronizationFailed?.Invoke(this, new EventArgs());
            }

            _synchronizeTimer.Stop();
            _synchronizeTimer.Start();

            IsSynchronizing = false;
            SynchronizationEnded?.Invoke(this, new EventArgs());
        }
    /// <summary>
    /// Gets the next packet from the server
    /// </summary>
    /// <returns></returns>
    public PacketData ReadNextPacket()
    {
        int packetId;

        byte[] payload;

        lock (_streamReadLock)
        {
            int         length = VarInt.ReadNext(ReadBytes);
            List <byte> buffer = new List <byte>();

            // check if data is compressed
            if (_compressionThreshold >= 0)
            {
                int dataLength = VarInt.ReadNext(ReadBytes);
                length -= VarInt.GetBytes(dataLength).Length;                   // remove size of data length from rest of packet length
                if (dataLength != 0)
                {
                    byte[] compressedBuffer = ReadBytes(length);
                    buffer.AddRange(ZlibStream.UncompressBuffer(compressedBuffer));
                }
                else
                {
                    buffer.AddRange(ReadBytes(length));
                }
            }
            else
            {
                buffer.AddRange(ReadBytes(length));
            }

            packetId = VarInt.ReadNext(buffer);
            payload  = buffer.ToArray();
        }

        // handles some stuff during login phase
        if (State == ProtocolState.LOGIN)
        {
            // handle compression packet
            if (packetId == (int)ClientboundIDs.LogIn_SetCompression)
            {
                _compressionThreshold = VarInt.ReadNext(new List <byte>(payload));
                return(ReadNextPacket());
            }

            // handle protocol encryption packet
            if (packetId == (int)ClientboundIDs.LogIn_EncryptionRequest)
            {
                var encRequestPkt = new EncryptionRequestPacket()
                {
                    Payload = payload
                };

                var aesSecret = CryptoHandler.GenerateSharedSecret();
                var authHash  = CryptoHandler.SHAHash(Encoding.ASCII.GetBytes(encRequestPkt.ServerID).Concat(aesSecret, encRequestPkt.PublicKey));

                Debug.Log($"Sending hash to Mojang servers: {authHash}");

                // check session with mojang
                if (!MojangAPI.JoinServer(authHash))
                {
                    throw new UnityException("Invalid session. (Try restarting game or relogging into Minecraft account)");
                }

                // use pub key to encrypt shared secret
                using (var rsaProvider = CryptoHandler.DecodeRSAPublicKey(encRequestPkt.PublicKey))
                {
                    byte[] encSecret = rsaProvider.Encrypt(aesSecret, false);
                    byte[] encToken  = rsaProvider.Encrypt(encRequestPkt.VerifyToken, false);

                    // respond to server with private key
                    var responsePkt = new EncryptionResponsePacket()
                    {
                        SharedSecret = encSecret,
                        VerifyToken  = encToken
                    };
                    WritePacket(responsePkt);


                    // enable aes encryption
                    _aesStream = new AesStream(Client.GetStream(), aesSecret);
                    _encrypted = true;

                    // read the next packet
                    return(ReadNextPacket());
                }
            }
        }

        return(new PacketData
        {
            ID = packetId,
            Payload = payload
        });
    }