Skip to content

Commit

Permalink
feat: levels data(files) encrypted
Browse files Browse the repository at this point in the history
  • Loading branch information
BIYUEHU committed Oct 5, 2024
1 parent 8e74cb2 commit 70af0d0
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 5 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/demo/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
Expand Down
58 changes: 58 additions & 0 deletions packages/demo/src-tauri/src/as_crypt.rs
Original file line number Diff line number Diff line change
@@ -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<String, &'static str> {
// 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);
}
}
11 changes: 9 additions & 2 deletions packages/demo/src-tauri/src/levels.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::as_crypt;
use base64::{engine::general_purpose::STANDARD, Engine as _};
use std::collections::HashMap;
use std::env;
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,
Expand Down Expand Up @@ -54,7 +57,7 @@ pub fn set_levels(name: &str, png_data: &str, dat_content: &str) -> Result<Strin

let mut dat_file = File::create(&dat_path).map_err(|e| e.to_string())?;
dat_file
.write_all(dat_content.as_bytes())
.write_all(as_crypt::encrypt(dat_content, LEVELS_CRYPTO_KEY).as_bytes())
.map_err(|e| e.to_string())?;

Ok("".to_string())
Expand Down Expand Up @@ -86,7 +89,11 @@ pub fn get_all_levels() -> Result<Vec<(String, String, String)>, 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())?,
);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/demo/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
12 changes: 10 additions & 2 deletions packages/misakura/src/class/levels/FileAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
Expand Down

0 comments on commit 70af0d0

Please sign in to comment.