diff --git a/Cargo.lock b/Cargo.lock index 97e423e..368394c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ name = "demo" version = "0.1.0" dependencies = [ "base64 0.22.1", + "ring", "serde", "serde_json", "tauri", @@ -2216,6 +2217,21 @@ dependencies = [ "windows 0.37.0", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2502,6 +2518,12 @@ dependencies = [ "system-deps 5.0.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3143,6 +3165,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.2" diff --git a/packages/demo/src-tauri/Cargo.toml b/packages/demo/src-tauri/Cargo.toml index 7cbb65f..1a8bf8b 100644 --- a/packages/demo/src-tauri/Cargo.toml +++ b/packages/demo/src-tauri/Cargo.toml @@ -11,7 +11,8 @@ edition = "2021" tauri-build = { version = "1", features = [] } [dependencies] -tauri = { version = "1", features = [ "dialog-all", +tauri = { version = "1", features = [ + "dialog-all", "fs-exists", "fs-read-dir", "fs-create-dir", @@ -24,6 +25,8 @@ tauri = { version = "1", features = [ "dialog-all", serde = { version = "1", features = ["derive"] } serde_json = "1" base64 = "0.22.1" +ring = "0.17.8" + [dev-dependencies] tempfile = "3.3" diff --git a/packages/demo/src-tauri/src/as_crypt.rs b/packages/demo/src-tauri/src/as_crypt.rs new file mode 100644 index 0000000..177839b --- /dev/null +++ b/packages/demo/src-tauri/src/as_crypt.rs @@ -0,0 +1,58 @@ +use base64::{engine::general_purpose::STANDARD, Engine as _}; + +pub fn encrypt(text: &str, key: &str) -> String { + let mut result = Vec::new(); + + // Convert the key to bytes + let key_bytes = key.as_bytes(); + + // Convert the plaintext to bytes + let text_bytes = text.as_bytes(); + + for (i, byte) in text_bytes.iter().enumerate() { + let key_byte = key_bytes[i % key_bytes.len()]; + // XOR the byte with the corresponding byte from the key + let encrypted_byte = byte ^ key_byte; + result.push(encrypted_byte); + } + + STANDARD.encode(&result) // Encode the result as Base64 +} + +pub fn decrypt(encrypted_text: &str, key: &str) -> Result { + // Decode the Base64 encoded string + let encrypted = STANDARD + .decode(encrypted_text) + .map_err(|_| "Base64 decode failed.")?; + + let mut result = Vec::new(); + + // Convert the key to bytes + let key_bytes = key.as_bytes(); + + for (i, byte) in encrypted.iter().enumerate() { + let key_byte = key_bytes[i % key_bytes.len()]; + // XOR the byte with the corresponding byte from the key + let decrypted_byte = byte ^ key_byte; + result.push(decrypted_byte); + } + + // Convert the decrypted bytes back to a String + String::from_utf8(result).map_err(|_| "Invalid UTF-8 sequence.") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_crypto() { + let key = "secret"; + let plaintext = "{\"script\":{\"entry\":\"main.mrs\",\"line\":49},\"background\":\"bg.png\",\"music\":{\"name\":\"1.mp3\",\"seconds\":47.46133333333333},\"characters\":[{\"identity\":\"soi\",\"name\":\"诗织\",\"figure\":\"/images/figure/shiori_shy.png\",\"position\":{\"type\":\"auto\",\"order\":1}},{\"identity\":\"self\",\"name\":\"“我”?\"},{\"identity\":\"author\",\"name\":\"夏叶的师傅\"}],\"speaker\":\"诗织\",\"values\":{\"constant\":{\"soi\":\"shiori.png\",\"soiL\":\"shiori_low.png\",\"soiM\":\"shiori_mid.png\",\"soiC\":\"shiori_close.png\",\"soiCS\":\"shiori_close_smile.png\",\"soiS\":\"shiori_smile.png\",\"soiO\":\"shiori_open.png\",\"soiOL\":\"shiori_open_low.png\",\"soiSh\":\"shiori_shy.png\",\"soiSq\":\"shiori_squint.png\"},\"variables\":{\"score\":50}}}\'"; + + let encrypted = encrypt(plaintext, key); + let decrypted = decrypt(&encrypted, key).expect("Decryption failed."); + + assert_eq!(decrypted, plaintext); + } +} diff --git a/packages/demo/src-tauri/src/levels.rs b/packages/demo/src-tauri/src/levels.rs index 3210da5..8f0019d 100644 --- a/packages/demo/src-tauri/src/levels.rs +++ b/packages/demo/src-tauri/src/levels.rs @@ -1,3 +1,4 @@ +use crate::as_crypt; use base64::{engine::general_purpose::STANDARD, Engine as _}; use std::collections::HashMap; use std::env; @@ -5,6 +6,8 @@ use std::fs::{self, File}; use std::io::{self, Read, Write}; use std::path::PathBuf; +const LEVELS_CRYPTO_KEY: &str = "きみがこの世界に生まれてきてくれて、本当に、よかった"; + struct MisakuraDirectory { base_directory: PathBuf, levels_directory: PathBuf, @@ -54,7 +57,7 @@ pub fn set_levels(name: &str, png_data: &str, dat_content: &str) -> Result Result, String> { let mut contents = String::new(); file.read_to_string(&mut contents) .map_err(|e| e.to_string())?; - dat_files.insert(file_stem, contents); + dat_files.insert( + file_stem, + as_crypt::decrypt(contents.as_str(), LEVELS_CRYPTO_KEY) + .map_err(|e| e.to_string())?, + ); } } } diff --git a/packages/demo/src-tauri/src/main.rs b/packages/demo/src-tauri/src/main.rs index 9b032b9..8dac8e1 100644 --- a/packages/demo/src-tauri/src/main.rs +++ b/packages/demo/src-tauri/src/main.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +mod as_crypt; mod levels; use levels::{get_all_levels, get_base_directory, set_levels}; diff --git a/packages/misakura/src/class/levels/FileAdapter.ts b/packages/misakura/src/class/levels/FileAdapter.ts index 142c22b..0e424c2 100644 --- a/packages/misakura/src/class/levels/FileAdapter.ts +++ b/packages/misakura/src/class/levels/FileAdapter.ts @@ -2,17 +2,25 @@ import { none } from '@kotori-bot/core' import LevelsAdapter from './LevelsAdapter' import type { LevelsData } from './LevelsAdapter' import { invoke } from '@tauri-apps/api' +import { logger } from '../../tools/logger' export class FileAdapter extends LevelsAdapter { public async getAll() { return ((await invoke('get_all_levels')) as [string, string, string][]) .map(([name, pngData, datContent]) => { const key = Number(name) - if (Number.isNaN(key)) return null - if (!pngData) return null + if (Number.isNaN(key)) { + logger.warn(`Invalid level name: ${name}`) + return null + } + if (!pngData) { + logger.warn(`Level ${name} has no icon data`) + return null + } try { return { ...JSON.parse(datContent), icon: pngData, position: key } } catch { + logger.warn(`Level ${name} has invalid data: ${datContent}`) return null } })