From 6d653a9843614d9a6bf881fc1b3b9514f66540b7 Mon Sep 17 00:00:00 2001 From: core Date: Sat, 6 Jan 2024 19:22:34 -0500 Subject: [PATCH] server save infrastructure --- Cargo.lock | 26 +++++++++++ server/src/component.rs | 5 ++ server/src/main.rs | 26 ++++++++++- server/src/packet.rs | 2 +- starkingdoms-client/src/hub.ts | 2 +- starkingdoms-client/src/protocol.ts | 2 +- starkingdoms-common/Cargo.toml | 6 ++- starkingdoms-common/src/lib.rs | 71 +++++++++++++++++++++++++++-- 8 files changed, 131 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fd1b0602c45319df91d5e7c5a72b7f177e6b3fd..2250851028214569b03ccb3759b972617a52fec7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3235,6 +3235,28 @@ version = "1.0.0" 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" @@ -3549,7 +3571,11 @@ dependencies = [ name = "starkingdoms-common" version = "0.1.0" dependencies = [ + "base64 0.21.5", + "hmac", + "rmp-serde", "serde", + "sha2", ] [[package]] diff --git a/server/src/component.rs b/server/src/component.rs index b13286a1e791ed4d442e20ee932f8373790eb678..b6f24d0881258d9232f53857cdf13a4a9d95db35 100644 --- a/server/src/component.rs +++ b/server/src/component.rs @@ -96,3 +96,8 @@ impl Default for ModuleTimer { Self::new() } } + +#[derive(Resource)] +pub struct AppKeys { + pub app_key: String, +} diff --git a/server/src/main.rs b/server/src/main.rs index d3cd7a071bb79f4d8f5f76598a86b7be90be8839..1dccb7574120a4cb859a36d2240eefb72f4b4027 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -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>, mut packet_event_send: ResMut>, + app_keys: Res, ) { 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::() * std::f32::consts::PI * 2. diff --git a/server/src/packet.rs b/server/src/packet.rs index f130758ee715d827987441abcaf5a3a4e92ebe3b..cc30c3906be6c27053d89f62a2e388f292814443 100644 --- a/server/src/packet.rs +++ b/server/src/packet.rs @@ -65,7 +65,7 @@ pub enum Packet { // serverbound ClientLogin { username: String, - jwt: Option, + save: Option, }, SendMessage { target: Option, diff --git a/starkingdoms-client/src/hub.ts b/starkingdoms-client/src/hub.ts index 218287814823968d5572c68ae205ef02e585cc1d..ff0a254fc0d5d6d548e0a56bedeee0b7e0718963 100644 --- a/starkingdoms-client/src/hub.ts +++ b/starkingdoms-client/src/hub.ts @@ -56,7 +56,7 @@ export async function hub_connect( t: PacketType.ClientLogin, c: { username, - jwt: null, + save: null, }, }; sendPacket(client, packet); diff --git a/starkingdoms-client/src/protocol.ts b/starkingdoms-client/src/protocol.ts index 55f7f3453840e59f1287efb09e7393729e6c41b0..c158df1c6a1ef0b257d9ff770cab9ad4c9dd035d 100644 --- a/starkingdoms-client/src/protocol.ts +++ b/starkingdoms-client/src/protocol.ts @@ -31,7 +31,7 @@ export interface Part { } export interface ClientLoginPacket { username: string; - jwt: string | null; + save: string | null; } export interface SpawnPlayerPacket { id: number; diff --git a/starkingdoms-common/Cargo.toml b/starkingdoms-common/Cargo.toml index 612a46fbc94e53fc5cf04628b995dfb7094e61a5..90875d2b826b939a42bca80c7ba52e6bf119a1c5 100644 --- a/starkingdoms-common/Cargo.toml +++ b/starkingdoms-common/Cargo.toml @@ -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 diff --git a/starkingdoms-common/src/lib.rs b/starkingdoms-common/src/lib.rs index 56f40dd70e0b15a94647d71c55a45f753ed8b2b7..d2116cd73da93c1774497df0b2ec1ed24ab458fa 100644 --- a/starkingdoms-common/src/lib.rs +++ b/starkingdoms-common/src/lib.rs @@ -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 . + +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, + mac: Vec, +} + +pub fn pack_savefile(key: &str, save_data: SaveData) -> String { + let mut mac: Hmac = 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> { + // << 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 = 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) }