forked from Berry-Dash/launcher
Securely (or as secure as it can be) store encryption keys
This commit is contained in:
8
src-tauri/src/keys.rs
Normal file
8
src-tauri/src/keys.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
pub struct Keys;
|
||||||
|
|
||||||
|
impl Keys {
|
||||||
|
pub const SERVER_RECEIVE_TRANSFER_KEY: &str = "";
|
||||||
|
pub const SERVER_SEND_TRANSFER_KEY: &str = "";
|
||||||
|
pub const CONFIG_ENCRYPTION_KEY: &str = "";
|
||||||
|
pub const VERSIONS_ENCRYPTION_KEY: &str = "";
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
|
||||||
|
mod keys;
|
||||||
|
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{File, create_dir_all},
|
fs::{File, create_dir_all},
|
||||||
@@ -12,6 +15,7 @@ use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
|
|||||||
use tauri_plugin_opener::OpenerExt;
|
use tauri_plugin_opener::OpenerExt;
|
||||||
use tokio::{io::AsyncWriteExt, task::spawn_blocking, time::timeout};
|
use tokio::{io::AsyncWriteExt, task::spawn_blocking, time::timeout};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
use keys::Keys;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::{fs, os::unix::fs::PermissionsExt};
|
use std::{fs, os::unix::fs::PermissionsExt};
|
||||||
@@ -207,6 +211,17 @@ fn download_leaderboard(app: AppHandle, content: String) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_keys_config(key: i8) -> String {
|
||||||
|
match key {
|
||||||
|
0 => Keys::SERVER_RECEIVE_TRANSFER_KEY.to_string(),
|
||||||
|
1 => Keys::SERVER_SEND_TRANSFER_KEY.to_string(),
|
||||||
|
2 => Keys::CONFIG_ENCRYPTION_KEY.to_string(),
|
||||||
|
3 => Keys::VERSIONS_ENCRYPTION_KEY.to_string(),
|
||||||
|
_ => "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
@@ -225,7 +240,8 @@ pub fn run() {
|
|||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
download,
|
download,
|
||||||
launch_game,
|
launch_game,
|
||||||
download_leaderboard
|
download_leaderboard,
|
||||||
|
get_keys_config
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
export enum Keys {
|
|
||||||
SERVER_RECEIVE_TRANSFER_KEY = '',
|
|
||||||
SERVER_SEND_TRANSFER_KEY = '',
|
|
||||||
CONFIG_ENCRYPTION_KEY = '',
|
|
||||||
VERSIONS_ENCRYPTION_KEY = ''
|
|
||||||
}
|
|
||||||
@@ -14,21 +14,22 @@ export default function Leaderboards () {
|
|||||||
async function refresh () {
|
async function refresh () {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLeaderboardData([])
|
setLeaderboardData([])
|
||||||
const launcherVersion = await app.getVersion()
|
try {
|
||||||
axios
|
const launcherVersion = await app.getVersion()
|
||||||
.get('https://berrydash.lncvrt.xyz/database/getTopPlayers.php', {
|
const response = await axios.get('https://berrydash.lncvrt.xyz/database/getTopPlayers.php', {
|
||||||
headers: {
|
headers: {
|
||||||
Requester: 'BerryDashLauncher',
|
Requester: 'BerryDashLauncher',
|
||||||
LauncherVersion: launcherVersion,
|
LauncherVersion: launcherVersion,
|
||||||
ClientPlatform: platform()
|
ClientPlatform: platform()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => {
|
const decrypted = await decrypt(response.data)
|
||||||
const decrypted = decrypt(res.data)
|
setLeaderboardData(JSON.parse(decrypted))
|
||||||
setLeaderboardData(JSON.parse(decrypted))
|
} catch (e) {
|
||||||
})
|
console.error('Error fetching leaderboard data:', e)
|
||||||
.catch(e => console.error('Error fetching leaderboard data:', e))
|
} finally {
|
||||||
.finally(() => setLoading(false))
|
setLoading(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadLeaderboard () {
|
function downloadLeaderboard () {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export default function Settings () {
|
|||||||
useState(false)
|
useState(false)
|
||||||
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
const [useWineOnUnixWhenNeeded, setUseWineOnUnixWhenNeeded] = useState(false)
|
||||||
const [allowNotifications, setAllowNotifications] = useState(false)
|
const [allowNotifications, setAllowNotifications] = useState(false)
|
||||||
|
const [loaded, setLoaded] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
@@ -15,45 +16,48 @@ export default function Settings () {
|
|||||||
setCheckForNewVersionOnLoad(config.settings.checkForNewVersionOnLoad)
|
setCheckForNewVersionOnLoad(config.settings.checkForNewVersionOnLoad)
|
||||||
setUseWineOnUnixWhenNeeded(config.settings.useWineOnUnixWhenNeeded)
|
setUseWineOnUnixWhenNeeded(config.settings.useWineOnUnixWhenNeeded)
|
||||||
setAllowNotifications(config.settings.allowNotifications)
|
setAllowNotifications(config.settings.allowNotifications)
|
||||||
|
setLoaded(true)
|
||||||
})()
|
})()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className='text-3xl ml-4 mt-4'>Settings</p>
|
<p className='text-3xl ml-4 mt-4'>Settings</p>
|
||||||
<div className='ml-4 mt-4 bg-[#161616] border border-[#242424] rounded-lg p-4 w-fit h-fit'>
|
{loaded && (
|
||||||
<Setting
|
<div className='ml-4 mt-4 bg-[#161616] border border-[#242424] rounded-lg p-4 w-fit h-fit'>
|
||||||
label='Check for new version on load'
|
<Setting
|
||||||
value={checkForNewVersionOnLoad}
|
label='Check for new version on load'
|
||||||
onChange={async () => {
|
value={checkForNewVersionOnLoad}
|
||||||
setCheckForNewVersionOnLoad(!checkForNewVersionOnLoad)
|
onChange={async () => {
|
||||||
const config = await readNormalConfig()
|
setCheckForNewVersionOnLoad(!checkForNewVersionOnLoad)
|
||||||
config.settings.checkForNewVersionOnLoad = !checkForNewVersionOnLoad
|
const config = await readNormalConfig()
|
||||||
await writeNormalConfig(config)
|
config.settings.checkForNewVersionOnLoad = !checkForNewVersionOnLoad
|
||||||
}}
|
await writeNormalConfig(config)
|
||||||
/>
|
}}
|
||||||
<Setting
|
/>
|
||||||
label='Allow sending notifications'
|
<Setting
|
||||||
value={allowNotifications}
|
label='Allow sending notifications'
|
||||||
onChange={async () => {
|
value={allowNotifications}
|
||||||
setAllowNotifications(!allowNotifications)
|
onChange={async () => {
|
||||||
const config = await readNormalConfig()
|
setAllowNotifications(!allowNotifications)
|
||||||
config.settings.allowNotifications = !allowNotifications
|
const config = await readNormalConfig()
|
||||||
await writeNormalConfig(config)
|
config.settings.allowNotifications = !allowNotifications
|
||||||
}}
|
await writeNormalConfig(config)
|
||||||
/>
|
}}
|
||||||
<Setting
|
/>
|
||||||
label='Use wine to launch Berry Dash when needed'
|
<Setting
|
||||||
value={useWineOnUnixWhenNeeded}
|
label='Use wine to launch Berry Dash when needed'
|
||||||
onChange={async () => {
|
value={useWineOnUnixWhenNeeded}
|
||||||
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
|
onChange={async () => {
|
||||||
const config = await readNormalConfig()
|
setUseWineOnUnixWhenNeeded(!useWineOnUnixWhenNeeded)
|
||||||
config.settings.useWineOnUnixWhenNeeded = !useWineOnUnixWhenNeeded
|
const config = await readNormalConfig()
|
||||||
await writeNormalConfig(config)
|
config.settings.useWineOnUnixWhenNeeded = !useWineOnUnixWhenNeeded
|
||||||
}}
|
await writeNormalConfig(config)
|
||||||
className={platform() == 'linux' || platform() == 'macos' ? '' : 'hidden'}
|
}}
|
||||||
/>
|
className={platform() == 'linux' || platform() == 'macos' ? '' : 'hidden'}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
writeFile
|
writeFile
|
||||||
} from '@tauri-apps/plugin-fs'
|
} from '@tauri-apps/plugin-fs'
|
||||||
import { decrypt, encrypt } from './Encryption'
|
import { decrypt, encrypt } from './Encryption'
|
||||||
import { Keys } from '../enums/Keys'
|
|
||||||
import { VersionsConfig } from '../types/VersionsConfig'
|
import { VersionsConfig } from '../types/VersionsConfig'
|
||||||
|
import { getKey } from './KeysHelper'
|
||||||
|
|
||||||
export async function readNormalConfig (): Promise<NormalConfig> {
|
export async function readNormalConfig (): Promise<NormalConfig> {
|
||||||
const version = await app.getVersion()
|
const version = await app.getVersion()
|
||||||
@@ -26,9 +26,9 @@ export async function readNormalConfig (): Promise<NormalConfig> {
|
|||||||
const file = await create('config.dat', options)
|
const file = await create('config.dat', options)
|
||||||
await file.write(
|
await file.write(
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(
|
await encrypt(
|
||||||
JSON.stringify(new NormalConfig(version)),
|
JSON.stringify(new NormalConfig(version)),
|
||||||
Keys.CONFIG_ENCRYPTION_KEY
|
await getKey(2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -37,7 +37,7 @@ export async function readNormalConfig (): Promise<NormalConfig> {
|
|||||||
}
|
}
|
||||||
const config = await readTextFile('config.dat', options)
|
const config = await readTextFile('config.dat', options)
|
||||||
return NormalConfig.import(
|
return NormalConfig.import(
|
||||||
JSON.parse(decrypt(config, Keys.CONFIG_ENCRYPTION_KEY))
|
JSON.parse(await decrypt(config, await getKey(2)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export async function writeNormalConfig (data: NormalConfig) {
|
|||||||
const file = await create('config.dat', options)
|
const file = await create('config.dat', options)
|
||||||
await file.write(
|
await file.write(
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(JSON.stringify(data), Keys.CONFIG_ENCRYPTION_KEY)
|
await encrypt(JSON.stringify(data), await getKey(2))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await file.close()
|
await file.close()
|
||||||
@@ -62,7 +62,7 @@ export async function writeNormalConfig (data: NormalConfig) {
|
|||||||
await writeFile(
|
await writeFile(
|
||||||
'config.dat',
|
'config.dat',
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(JSON.stringify(data), Keys.CONFIG_ENCRYPTION_KEY)
|
await encrypt(JSON.stringify(data), await getKey(2))
|
||||||
),
|
),
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
@@ -83,9 +83,9 @@ export async function readVersionsConfig (): Promise<VersionsConfig> {
|
|||||||
const file = await create('versions.dat', options)
|
const file = await create('versions.dat', options)
|
||||||
await file.write(
|
await file.write(
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(
|
await encrypt(
|
||||||
JSON.stringify(new VersionsConfig(version)),
|
JSON.stringify(new VersionsConfig(version)),
|
||||||
Keys.VERSIONS_ENCRYPTION_KEY
|
await getKey(3)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -94,7 +94,7 @@ export async function readVersionsConfig (): Promise<VersionsConfig> {
|
|||||||
}
|
}
|
||||||
const config = await readTextFile('versions.dat', options)
|
const config = await readTextFile('versions.dat', options)
|
||||||
return VersionsConfig.import(
|
return VersionsConfig.import(
|
||||||
JSON.parse(decrypt(config, Keys.VERSIONS_ENCRYPTION_KEY))
|
JSON.parse(await decrypt(config, await getKey(3)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ export async function writeVersionsConfig (data: VersionsConfig) {
|
|||||||
const file = await create('versions.dat', options)
|
const file = await create('versions.dat', options)
|
||||||
await file.write(
|
await file.write(
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(JSON.stringify(data), Keys.VERSIONS_ENCRYPTION_KEY)
|
await encrypt(JSON.stringify(data), await getKey(3))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await file.close()
|
await file.close()
|
||||||
@@ -119,7 +119,7 @@ export async function writeVersionsConfig (data: VersionsConfig) {
|
|||||||
await writeFile(
|
await writeFile(
|
||||||
'versions.dat',
|
'versions.dat',
|
||||||
new TextEncoder().encode(
|
new TextEncoder().encode(
|
||||||
encrypt(JSON.stringify(data), Keys.VERSIONS_ENCRYPTION_KEY)
|
await encrypt(JSON.stringify(data), await getKey(3))
|
||||||
),
|
),
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import CryptoJS from 'crypto-js'
|
import CryptoJS from 'crypto-js'
|
||||||
import { Keys } from '../enums/Keys'
|
import { getKey } from './KeysHelper'
|
||||||
|
|
||||||
export function encrypt (plainText: string, key: string = Keys.SERVER_SEND_TRANSFER_KEY): string {
|
export async function encrypt (
|
||||||
|
plainText: string,
|
||||||
|
key?: string
|
||||||
|
): Promise<string> {
|
||||||
|
if (!key) key = await getKey(1)
|
||||||
const iv = CryptoJS.lib.WordArray.random(16)
|
const iv = CryptoJS.lib.WordArray.random(16)
|
||||||
const encrypted = CryptoJS.AES.encrypt(
|
const encrypted = CryptoJS.AES.encrypt(
|
||||||
plainText,
|
plainText,
|
||||||
@@ -16,7 +20,8 @@ export function encrypt (plainText: string, key: string = Keys.SERVER_SEND_TRANS
|
|||||||
return CryptoJS.enc.Base64.stringify(combined)
|
return CryptoJS.enc.Base64.stringify(combined)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decrypt (dataB64: string, key: string = Keys.SERVER_RECEIVE_TRANSFER_KEY): string {
|
export async function decrypt (dataB64: string, key?: string): Promise<string> {
|
||||||
|
if (!key) key = await getKey(0)
|
||||||
const data = CryptoJS.enc.Base64.parse(dataB64)
|
const data = CryptoJS.enc.Base64.parse(dataB64)
|
||||||
const iv = CryptoJS.lib.WordArray.create(data.words.slice(0, 4), 16)
|
const iv = CryptoJS.lib.WordArray.create(data.words.slice(0, 4), 16)
|
||||||
const ciphertext = CryptoJS.lib.WordArray.create(
|
const ciphertext = CryptoJS.lib.WordArray.create(
|
||||||
@@ -35,6 +40,6 @@ export function decrypt (dataB64: string, key: string = Keys.SERVER_RECEIVE_TRAN
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const result = decrypted.toString(CryptoJS.enc.Utf8)
|
const result = decrypted.toString(CryptoJS.enc.Utf8)
|
||||||
if (!result) throw new Error(encrypt('-997'))
|
if (!result) throw new Error(await encrypt('-997'))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/util/KeysHelper.ts
Normal file
11
src/util/KeysHelper.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
|
export async function getKey(key: number): Promise<string> {
|
||||||
|
try {
|
||||||
|
const message = await invoke('get_keys_config', { key });
|
||||||
|
return message as string;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get key from Tauri backend', error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user