From 0d3b575903cf757e1c2809d0434bf014e8a7604e Mon Sep 17 00:00:00 2001 From: asesidaa <1061472754@qq.com> Date: Thu, 5 Oct 2023 02:09:37 +0800 Subject: [PATCH] Update to support encrypted datatables, all on server side --- TaikoLocalServer/Common/Constants.cs | 11 ++- TaikoLocalServer/Services/GameDataService.cs | 86 +++++++++++++------- TaikoLocalServer/TaikoLocalServer.csproj | 12 +++ TaikoWebUI/Services/GameDataService.cs | 31 ++----- 4 files changed, 80 insertions(+), 60 deletions(-) diff --git a/TaikoLocalServer/Common/Constants.cs b/TaikoLocalServer/Common/Constants.cs index 6f4653b..0588daf 100644 --- a/TaikoLocalServer/Common/Constants.cs +++ b/TaikoLocalServer/Common/Constants.cs @@ -10,11 +10,14 @@ public static class Constants public const string DEFAULT_DB_NAME = "taiko.db3"; - public const string MUSIC_INFO_FILE_NAME = "musicinfo.json"; - public const string MUSIC_INFO_COMPRESSED_FILE_NAME = "musicinfo.bin"; - - public const string MUSIC_ATTRIBUTE_FILE_NAME = "music_attribute.json"; + public const string MUSIC_INFO_BASE_NAME = "musicinfo"; + public const string MUSIC_ATTRIBUTE_BASE_NAME = "music_attribute"; public const string MUSIC_ATTRIBUTE_COMPRESSED_FILE_NAME = "music_attribute.bin"; + + + public const string WORDLIST_BASE_NAME = "wordlist"; + public const string MUSIC_ORDER_BASE_NAME = "music_order"; + public const string DAN_DATA_FILE_NAME = "dan_data.json"; public const string INTRO_DATA_FILE_NAME = "intro_data.json"; diff --git a/TaikoLocalServer/Services/GameDataService.cs b/TaikoLocalServer/Services/GameDataService.cs index a440939..0c93a54 100644 --- a/TaikoLocalServer/Services/GameDataService.cs +++ b/TaikoLocalServer/Services/GameDataService.cs @@ -4,6 +4,8 @@ using SharedProject.Models; using SharedProject.Utils; using Swan.Mapping; using System.Collections.Immutable; +using System.IO.Compression; +using System.Security.Cryptography; using System.Text.Json; using TaikoLocalServer.Settings; using Throw; @@ -120,10 +122,19 @@ public class GameDataService : IGameDataService public async Task InitializeAsync() { var dataPath = PathHelper.GetDataPath(); - var musicInfoPath = Path.Combine(dataPath, Constants.MUSIC_INFO_FILE_NAME); - var compressedMusicInfoPath = Path.Combine(dataPath, Constants.MUSIC_INFO_COMPRESSED_FILE_NAME); - var musicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME); - var compressedMusicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_COMPRESSED_FILE_NAME); + + var musicInfoPath = Path.Combine(dataPath, $"{Constants.MUSIC_INFO_BASE_NAME}.json"); + var enctyptedInfo = Path.Combine(dataPath, $"{Constants.MUSIC_INFO_BASE_NAME}.bin"); + + var musicAttributePath = Path.Combine(dataPath, $"{Constants.MUSIC_ATTRIBUTE_BASE_NAME}.json"); + var encryptedAttribute = Path.Combine(dataPath, $"{Constants.MUSIC_ATTRIBUTE_BASE_NAME}.bin"); + + var wordlistPath = Path.Combine(dataPath, $"{Constants.WORDLIST_BASE_NAME}.json"); + var encryptedWordlist = Path.Combine(dataPath, $"{Constants.WORDLIST_BASE_NAME}.bin"); + + var musicOrderPath = Path.Combine(dataPath, $"{Constants.MUSIC_ORDER_BASE_NAME}.json"); + var encryptedMusicOrder = Path.Combine(dataPath, $"{Constants.MUSIC_ORDER_BASE_NAME}.bin"); + var danDataPath = Path.Combine(dataPath, settings.DanDataFileName); var gaidenDataPath = Path.Combine(dataPath, settings.GaidenDataFileName); var songIntroDataPath = Path.Combine(dataPath, settings.IntroDataFileName); @@ -134,14 +145,32 @@ public class GameDataService : IGameDataService var lockedSongsDataPath = Path.Combine(dataPath, settings.LockedSongsDataFileName); var qrCodeDataPath = Path.Combine(dataPath, settings.QRCodeDataFileName); - if (File.Exists(compressedMusicInfoPath)) + if (File.Exists(enctyptedInfo)) { - TryDecompressMusicInfo(); + DecryptDataTable(enctyptedInfo, musicInfoPath); } - if (File.Exists(compressedMusicAttributePath)) + if (File.Exists(encryptedAttribute)) { - TryDecompressMusicAttribute(); + DecryptDataTable(encryptedAttribute, musicAttributePath); } + if (File.Exists(encryptedWordlist)) + { + DecryptDataTable(encryptedWordlist, wordlistPath); + } + if (File.Exists(encryptedMusicOrder)) + { + DecryptDataTable(encryptedMusicOrder, musicOrderPath); + } + + if (!File.Exists(wordlistPath)) + { + throw new FileNotFoundException("Wordlist file not found!"); + } + if (!File.Exists(musicOrderPath)) + { + throw new FileNotFoundException("Music order file not found!"); + } + await using var musicInfoFile = File.OpenRead(musicInfoPath); await using var musicAttributeFile = File.OpenRead(musicAttributePath); await using var danDataFile = File.OpenRead(danDataPath); @@ -189,29 +218,26 @@ public class GameDataService : IGameDataService InitializeQRCodeData(qrCodeData); } - private static void TryDecompressMusicInfo() + private static void DecryptDataTable(string inputFileName, string outputFileName) { - var dataPath = PathHelper.GetDataPath(); - var musicInfoPath = Path.Combine(dataPath, Constants.MUSIC_INFO_FILE_NAME); - var compressedMusicInfoPath = Path.Combine(dataPath, Constants.MUSIC_INFO_COMPRESSED_FILE_NAME); - - using var compressed = File.Open(compressedMusicInfoPath, FileMode.Open); - using var output = File.Create(musicInfoPath); - - GZip.Decompress(compressed, output, true); - } - - private static void TryDecompressMusicAttribute() - { - var dataPath = PathHelper.GetDataPath(); - var musicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME); - var compressedMusicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_COMPRESSED_FILE_NAME); - - using var compressed = File.Open(compressedMusicAttributePath, FileMode.Open); - using var output = File.Create(musicAttributePath); - - GZip.Decompress(compressed, output, true); - } + var aes = Aes.Create(); + aes.Mode = CipherMode.CBC; + aes.KeySize = 256; + aes.Padding = PaddingMode.PKCS7; + aes.Key = Convert.FromHexString("3530304242323633353537423431384139353134383346433246464231354534"); + var iv = new byte[16]; + using var fs = File.OpenRead(inputFileName); + var count = fs.Read(iv, 0, 16); + count.Throw("Read IV for datatable failed!").IfNotEquals(16); + aes.IV = iv; + using var cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read); + using var ms = new MemoryStream(); + cs.CopyTo(ms); + ms.Position = 0; + using var gz = new GZipStream(ms, CompressionMode.Decompress); + using var output = File.Create(outputFileName); + gz.CopyTo(output); + } private void InitializeIntroData(List? introData) { diff --git a/TaikoLocalServer/TaikoLocalServer.csproj b/TaikoLocalServer/TaikoLocalServer.csproj index d19e469..301d211 100644 --- a/TaikoLocalServer/TaikoLocalServer.csproj +++ b/TaikoLocalServer/TaikoLocalServer.csproj @@ -87,6 +87,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/TaikoWebUI/Services/GameDataService.cs b/TaikoWebUI/Services/GameDataService.cs index 977fe54..0f16824 100644 --- a/TaikoWebUI/Services/GameDataService.cs +++ b/TaikoWebUI/Services/GameDataService.cs @@ -1,5 +1,7 @@ using System.Collections.Immutable; +using System.IO.Compression; using System.Net; +using System.Security.Cryptography; using System.Text.Json; using ICSharpCode.SharpZipLib.GZip; using Swan.Mapping; @@ -54,32 +56,9 @@ public class GameDataService : IGameDataService private async Task GetData(string dataBaseUrl, string fileBaseName) where T : notnull { - T? data; - try - { - data = await client.GetFromJsonAsync($"{dataBaseUrl}/data/{fileBaseName}.json"); - data.ThrowIfNull(); - return data; - } - catch (HttpRequestException e) - { - if (e.StatusCode != HttpStatusCode.NotFound) - { - throw; - } - await using var compressed = await client.GetStreamAsync($"{dataBaseUrl}/data/{fileBaseName}.bin"); - await using var gZipInputStream = new GZipInputStream(compressed); - using var decompressed = new MemoryStream(); - - // Decompress - await gZipInputStream.CopyToAsync(decompressed); - - // Reset stream for reading - decompressed.Position = 0; - data = await JsonSerializer.DeserializeAsync(decompressed); - data.ThrowIfNull(); - return data; - } + var data = await client.GetFromJsonAsync($"{dataBaseUrl}/data/{fileBaseName}.json"); + data.ThrowIfNull(); + return data; } public string GetMusicNameBySongId(uint songId)