M Cargo.lock => Cargo.lock +26 -0
@@ 3236,6 3236,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b"
[[package]]
+name = "rmp"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
name = "robust"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 3549,7 3571,11 @@ dependencies = [
name = "starkingdoms-common"
version = "0.1.0"
dependencies = [
+ "base64 0.21.5",
+ "hmac",
+ "rmp-serde",
"serde",
+ "sha2",
]
[[package]]
M server/src/component.rs => server/src/component.rs +5 -0
@@ 96,3 96,8 @@ impl Default for ModuleTimer {
Self::new()
}
}
+
+#[derive(Resource)]
+pub struct AppKeys {
+ pub app_key: String,
+}
M server/src/main.rs => server/src/main.rs +25 -1
@@ 27,6 27,7 @@ use component::Input;
use component::*;
use packet::*;
use rand::Rng;
+use starkingdoms_common::unpack_savefile;
pub mod component;
pub mod macros;
@@ 53,7 54,11 @@ const LANDING_THRUSTER_FORCE: f32 = 5.;
const FREE_MODULE_CAP: usize = 30;
fn main() {
+ // read the key in
+ let key = std::fs::read_to_string("/etc/starkingdoms/app_key").unwrap();
+
App::new()
+ .insert_resource(AppKeys { app_key: key })
.insert_resource(TwiteServerConfig {
addr: Ipv4Addr::new(0, 0, 0, 0),
port: 3000,
@@ 225,6 230,7 @@ fn on_message(
>,
mut packet_recv: Local<ManualEventReader<ServerEvent>>,
mut packet_event_send: ResMut<Events<ServerEvent>>,
+ app_keys: Res<AppKeys>,
) {
let mut event_queue = Vec::new();
for ev in packet_recv.read(&packet_event_send) {
@@ 233,7 239,25 @@ fn on_message(
let packet: Packet = err_or_cont!(serde_json::from_str(&data));
match packet {
- Packet::ClientLogin { username, .. } => {
+ Packet::ClientLogin { username, save } => {
+ if let Some(save) = save {
+ // attempt to decode the savefile
+ if let Ok(savefile) = unpack_savefile(&app_keys.app_key, save) {
+ // HEY! GHOSTLY! THIS SAVE FILE IS VALID! PLEASE LOAD IT!
+ // THANKS!
+ } else {
+ let packet = Packet::Message {
+ message_type: packet::MessageType::Error,
+ actor: "SERVER".to_string(),
+ content: "Savefile signature corrupted or inner data invalid. Save was not loaded. Contact StarKingdoms staff for assistance.".to_string(),
+ };
+ let buf = serde_json::to_vec(&packet).unwrap();
+ event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf));
+ }
+ } else {
+ // nothing to do
+ }
+
let angle: f32 = {
let mut rng = rand::thread_rng();
rng.gen::<f32>() * std::f32::consts::PI * 2.
M server/src/packet.rs => server/src/packet.rs +1 -1
@@ 65,7 65,7 @@ pub enum Packet {
// serverbound
ClientLogin {
username: String,
- jwt: Option<String>,
+ save: Option<String>,
},
SendMessage {
target: Option<String>,
M starkingdoms-client/src/hub.ts => starkingdoms-client/src/hub.ts +1 -1
@@ 56,7 56,7 @@ export async function hub_connect(
t: PacketType.ClientLogin,
c: {
username,
- jwt: null,
+ save: null,
},
};
sendPacket(client, packet);
M starkingdoms-client/src/protocol.ts => starkingdoms-client/src/protocol.ts +1 -1
@@ 31,7 31,7 @@ export interface Part {
}
export interface ClientLoginPacket {
username: string;
- jwt: string | null;
+ save: string | null;
}
export interface SpawnPlayerPacket {
id: number;
M starkingdoms-common/Cargo.toml => starkingdoms-common/Cargo.toml +5 -1
@@ 6,4 6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-serde = { version = "1", features = ["derive"] }>
\ No newline at end of file
+serde = { version = "1", features = ["derive"] }
+rmp-serde = "1"
+hmac = "0.12"
+sha2 = "0.10"
+base64 = "0.21"<
\ No newline at end of file
M starkingdoms-common/src/lib.rs => starkingdoms-common/src/lib.rs +67 -4
@@ 1,7 1,70 @@
+// StarKingdoms.IO, a browser game about drifting through space
+// Copyright (C) 2024 ghostly_zsh, TerraMaster85, core
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+use base64::Engine;
+use hmac::{Hmac, Mac};
use serde::{Deserialize, Serialize};
+use sha2::Sha256;
+use std::error::Error;
+
+#[derive(Serialize, Deserialize)]
+pub struct SaveData {
+ // ----------------------------------------------------------------------
+ // HEY YOU
+ // YES YOU
+ // GHOSTLY
+ // FILL THIS WITH STUFF
+ // ----------------------------------------------------------------------
+ // THANKS! -core
+}
+
+// no touchy. this is the struct that savefiles are actually represented in
+#[derive(Serialize, Deserialize)]
+pub struct Savefile {
+ data_msgpack: Vec<u8>,
+ mac: Vec<u8>,
+}
+
+pub fn pack_savefile(key: &str, save_data: SaveData) -> String {
+ let mut mac: Hmac<Sha256> = Hmac::new_from_slice(key.as_bytes()).unwrap();
+
+ let save_data_bytes = rmp_serde::to_vec(&save_data).unwrap();
+ mac.update(&save_data_bytes);
+ let mc_code = mac.finalize().into_bytes();
+
+ let save_file = Savefile {
+ data_msgpack: save_data_bytes,
+ mac: mc_code.to_vec(),
+ };
+
+ let final_bytes = rmp_serde::to_vec(&save_file).unwrap();
+
+ base64::engine::general_purpose::STANDARD.encode(final_bytes)
+}
+pub fn unpack_savefile(key: &str, file: String) -> Result<SaveData, Box<dyn Error>> {
+ // << reverse! <<
+ let savefile_bytes = base64::engine::general_purpose::STANDARD.decode(file)?;
+
+ let save_file: Savefile = rmp_serde::from_slice(&savefile_bytes)?;
+
+ let mut mac: Hmac<Sha256> = Hmac::new_from_slice(key.as_bytes()).unwrap();
+ mac.update(&save_file.data_msgpack);
+ mac.verify_slice(&save_file.mac)?;
+
+ let save_data = rmp_serde::from_slice(&save_file.data_msgpack)?;
-#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
-#[serde(tag = "version", content = "data")]
-pub enum PlayerSaveFile {
- V1 {},
+ Ok(save_data)
}