mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-05 12:17:45 +00:00
Move encryption stuffs into their own module
This commit is contained in:
parent
cdc250fca1
commit
51e0f0680e
4 changed files with 90 additions and 68 deletions
|
@ -1,4 +1,4 @@
|
||||||
use kawari::packet::{parse_packet, State};
|
use kawari::packet::{State, parse_packet};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
|
54
src/encryption.rs
Normal file
54
src/encryption.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use binrw::{BinRead, BinResult};
|
||||||
|
use physis::blowfish::Blowfish;
|
||||||
|
|
||||||
|
const GAME_VERSION: u16 = 7000;
|
||||||
|
|
||||||
|
pub fn generate_encryption_key(key: &[u8], phrase: &str) -> [u8; 16] {
|
||||||
|
let mut base_key = vec![0x78, 0x56, 0x34, 0x12];
|
||||||
|
base_key.extend_from_slice(&key);
|
||||||
|
base_key.extend_from_slice(&GAME_VERSION.to_le_bytes());
|
||||||
|
base_key.extend_from_slice(&[0; 2]); // padding (possibly for game version?)
|
||||||
|
base_key.extend_from_slice(&phrase.as_bytes());
|
||||||
|
|
||||||
|
md5::compute(&base_key).0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encryption_key() {
|
||||||
|
let key = generate_encryption_key([0x00, 0x00, 0x00, 0x00], "foobar");
|
||||||
|
assert_eq!(
|
||||||
|
key,
|
||||||
|
[
|
||||||
|
169, 78, 235, 31, 57, 151, 26, 74, 250, 196, 1, 120, 206, 173, 202, 48
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[binrw::parser(reader, endian)]
|
||||||
|
pub(crate) fn decrypt<T>(size: u32, encryption_key: Option<&[u8]>) -> BinResult<T>
|
||||||
|
where
|
||||||
|
for<'a> T: BinRead<Args<'a> = ()> + 'a,
|
||||||
|
{
|
||||||
|
let Some(encryption_key) = encryption_key else {
|
||||||
|
panic!("This segment type is encrypted and no key was provided!");
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = size - 16; // 16 = header size
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
data.resize(size as usize, 0x0);
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
|
let blowfish = Blowfish::new(encryption_key);
|
||||||
|
let decrypted_data = blowfish.decrypt(&data).unwrap();
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(&decrypted_data);
|
||||||
|
T::read_options(&mut cursor, endian, ())
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use rand::Rng;
|
||||||
use rand::distributions::Alphanumeric;
|
use rand::distributions::Alphanumeric;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod encryption;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod patchlist;
|
pub mod patchlist;
|
||||||
|
|
||||||
|
|
101
src/packet.rs
101
src/packet.rs
|
@ -1,8 +1,17 @@
|
||||||
use std::{fs::write, io::Cursor, time::{SystemTime, UNIX_EPOCH}};
|
use std::{
|
||||||
|
fs::write,
|
||||||
|
io::Cursor,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
use binrw::{binrw, helpers::until_eof, BinRead, BinResult, BinWrite};
|
use binrw::{BinRead, BinResult, BinWrite, binrw, helpers::until_eof};
|
||||||
use physis::blowfish::Blowfish;
|
use physis::blowfish::Blowfish;
|
||||||
use tokio::{io::{AsyncWriteExt, WriteHalf}, net::TcpStream};
|
use tokio::{
|
||||||
|
io::{AsyncWriteExt, WriteHalf},
|
||||||
|
net::TcpStream,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::encryption::{decrypt, generate_encryption_key};
|
||||||
|
|
||||||
pub(crate) fn read_bool_from<T: std::convert::From<u8> + std::cmp::PartialEq>(x: T) -> bool {
|
pub(crate) fn read_bool_from<T: std::convert::From<u8> + std::cmp::PartialEq>(x: T) -> bool {
|
||||||
x == T::from(1u8)
|
x == T::from(1u8)
|
||||||
|
@ -30,29 +39,7 @@ enum ConnectionType {
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct IPCSegment {
|
struct IPCSegment {
|
||||||
unk: u32
|
unk: u32,
|
||||||
}
|
|
||||||
|
|
||||||
#[binrw::parser(reader, endian)]
|
|
||||||
pub(crate) fn decrypt<T>(size: u32, encryption_key: Option<&[u8]>) -> BinResult<T>
|
|
||||||
where
|
|
||||||
for<'a> T: BinRead<Args<'a> = ()> + 'a
|
|
||||||
{
|
|
||||||
let Some(encryption_key) = encryption_key else {
|
|
||||||
panic!("This segment type is encrypted and no key was provided!");
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = size - 16; // 16 = header size
|
|
||||||
|
|
||||||
let mut data = Vec::new();
|
|
||||||
data.resize(size as usize, 0x0);
|
|
||||||
reader.read_exact(&mut data)?;
|
|
||||||
|
|
||||||
let blowfish = Blowfish::new(encryption_key);
|
|
||||||
let decrypted_data = blowfish.decrypt(&data).unwrap();
|
|
||||||
|
|
||||||
let mut cursor = Cursor::new(&decrypted_data);
|
|
||||||
T::read_options(&mut cursor, endian, ())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -82,7 +69,7 @@ enum SegmentType {
|
||||||
#[brw(magic = 0x0Au32)]
|
#[brw(magic = 0x0Au32)]
|
||||||
InitializationEncryptionResponse {
|
InitializationEncryptionResponse {
|
||||||
#[br(count = 0x280)]
|
#[br(count = 0x280)]
|
||||||
data: Vec<u8>
|
data: Vec<u8>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,11 +105,12 @@ struct PacketSegment {
|
||||||
impl PacketSegment {
|
impl PacketSegment {
|
||||||
fn calc_size(&self) -> u32 {
|
fn calc_size(&self) -> u32 {
|
||||||
let header = std::mem::size_of::<u32>() * 4;
|
let header = std::mem::size_of::<u32>() * 4;
|
||||||
return header as u32 + match &self.segment_type {
|
return header as u32
|
||||||
SegmentType::InitializeEncryption { .. } => 616,
|
+ match &self.segment_type {
|
||||||
SegmentType::InitializationEncryptionResponse { .. } => 640,
|
SegmentType::InitializeEncryption { .. } => 616,
|
||||||
SegmentType::IPC { .. } => todo!(),
|
SegmentType::InitializationEncryptionResponse { .. } => 640,
|
||||||
};
|
SegmentType::IPC { .. } => todo!(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,20 +175,23 @@ async fn send_packet(socket: &mut WriteHalf<TcpStream>, segments: &[PacketSegmen
|
||||||
|
|
||||||
// temporary
|
// temporary
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub client_key: Option<[u8; 16]>
|
pub client_key: Option<[u8; 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn parse_packet(socket: &mut WriteHalf<TcpStream>, data: &[u8], state: &mut State) {
|
pub async fn parse_packet(socket: &mut WriteHalf<TcpStream>, data: &[u8], state: &mut State) {
|
||||||
let mut cursor = Cursor::new(data);
|
let mut cursor = Cursor::new(data);
|
||||||
|
|
||||||
match Packet::read_le_args(&mut cursor, (state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),)) {
|
match Packet::read_le_args(
|
||||||
|
&mut cursor,
|
||||||
|
(state.client_key.as_ref().map(|s: &[u8; 16]| s.as_slice()),),
|
||||||
|
) {
|
||||||
Ok(packet) => {
|
Ok(packet) => {
|
||||||
println!("{:#?}", packet);
|
println!("{:#?}", packet);
|
||||||
|
|
||||||
if packet.header.size as usize != data.len() {
|
if packet.header.size as usize != data.len() {
|
||||||
dump(
|
dump(
|
||||||
"Packet size mismatch between what we're given and the header!",
|
"Packet size mismatch between what we're given and the header!",
|
||||||
data,
|
data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,46 +208,22 @@ pub async fn parse_packet(socket: &mut WriteHalf<TcpStream>, data: &[u8], state:
|
||||||
let response_packet = PacketSegment {
|
let response_packet = PacketSegment {
|
||||||
source_actor: 0,
|
source_actor: 0,
|
||||||
target_actor: 0,
|
target_actor: 0,
|
||||||
segment_type: SegmentType::InitializationEncryptionResponse {
|
segment_type: SegmentType::InitializationEncryptionResponse { data },
|
||||||
data
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
send_packet(socket, &[response_packet]).await;
|
send_packet(socket, &[response_packet]).await;
|
||||||
},
|
}
|
||||||
SegmentType::InitializationEncryptionResponse { .. } => panic!("The server is recieving a response packet!"),
|
SegmentType::InitializationEncryptionResponse { .. } => {
|
||||||
|
panic!("The server is recieving a response packet!")
|
||||||
|
}
|
||||||
SegmentType::IPC { .. } => {
|
SegmentType::IPC { .. } => {
|
||||||
// decrypt
|
// decrypt
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("{err}");
|
println!("{err}");
|
||||||
dump("Failed to parse packet!", data);
|
dump("Failed to parse packet!", data);
|
||||||
},
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const GAME_VERSION: u16 = 7000;
|
|
||||||
|
|
||||||
pub fn generate_encryption_key(key: &[u8], phrase: &str) -> [u8; 16] {
|
|
||||||
let mut base_key = vec![0x78, 0x56, 0x34, 0x12];
|
|
||||||
base_key.extend_from_slice(&key);
|
|
||||||
base_key.extend_from_slice(&GAME_VERSION.to_le_bytes());
|
|
||||||
base_key.extend_from_slice(&[0; 2]); // padding (possibly for game version?)
|
|
||||||
base_key.extend_from_slice(&phrase.as_bytes());
|
|
||||||
|
|
||||||
md5::compute(&base_key).0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encryption_key() {
|
|
||||||
let key = generate_encryption_key([0x00, 0x00, 0x00, 0x00], "foobar");
|
|
||||||
assert_eq!(key, [169, 78, 235, 31, 57, 151, 26, 74, 250, 196, 1, 120, 206, 173, 202, 48]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue