diff --git a/Cargo.toml b/Cargo.toml index 7fb578b..25803c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ authors = ["Jake Probst "] edition = "2018" [dependencies] +rand = "0.6.5" psopacket = { path = "psopacket" } \ No newline at end of file diff --git a/src/crypto/bb.rs b/src/crypto/bb.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs new file mode 100644 index 0000000..9267e49 --- /dev/null +++ b/src/crypto/mod.rs @@ -0,0 +1,14 @@ +mod pc; + + +#[derive(Debug)] +pub enum CipherError { + InvalidSize +} + + + +trait PSOCipher { + fn encrypt(&mut self, data: &Vec) -> Result, CipherError>; + fn decrypt(&mut self, data: &Vec) -> Result, CipherError>; +} diff --git a/src/crypto/pc.rs b/src/crypto/pc.rs new file mode 100644 index 0000000..09f1675 --- /dev/null +++ b/src/crypto/pc.rs @@ -0,0 +1,148 @@ +// implementation taken from kohle's newserv +// https://github.com/fuzziqersoftware/newserv/ + +use crate::crypto::{PSOCipher, CipherError}; +use std::num::Wrapping as W; + +const PC_STREAM_LENGTH: usize = 57; + +struct PSOPCCipher { + stream: [u32; PC_STREAM_LENGTH], + offset: u16, +} + +impl PSOPCCipher { + pub fn new(seed: u32) -> PSOPCCipher { + let mut esi: W; + let mut ebx: W; + let mut edi: W; + let mut eax: W; + let mut edx: W; + let mut var1: W; + + let mut stream: [u32; PC_STREAM_LENGTH] = [0; PC_STREAM_LENGTH]; + + esi = W(1); + ebx = W(seed); + edi = W(0x15); + stream[56] = ebx.0; + stream[55] = ebx.0; + while edi <= W(0x46E) { + eax = edi; + var1 = eax / W(55); + edx = eax - (var1 * W(55)); + ebx = ebx - esi; + edi = edi + W(0x15); + stream[edx.0 as usize] = esi.0; + esi = ebx; + ebx = W(stream[edx.0 as usize]); + } + + let mut cipher = PSOPCCipher { + stream: stream, + offset: 1, + }; + + for _ in 0..5 { + cipher.update_stream(); + } + + cipher + } + + fn update_stream(&mut self) { + let mut esi: W; + let mut edi: W; + let mut eax: W; + let mut ebp: W; + let mut edx: W; + + edi = W(1); + edx = W(0x18); + eax = edi; + while edx > W(0) { + esi = W(self.stream[eax.0 as usize + 0x1F]); + ebp = W(self.stream[eax.0 as usize]); + ebp = ebp - esi; + self.stream[eax.0 as usize] = ebp.0; + eax += W(1); + edx -= W(1); + } + edi = W(0x19); + edx = W(0x1F); + eax = edi; + while edx > W(0) { + esi = W(self.stream[eax.0 as usize - 0x18]); + ebp = W(self.stream[eax.0 as usize]); + ebp = ebp - esi; + self.stream[eax.0 as usize] = ebp.0; + eax += W(1); + edx -= W(1); + } + } + + fn next(&mut self) -> u32 { + if self.offset as usize == PC_STREAM_LENGTH { + self.update_stream(); + self.offset = 1; + } + + let result = self.stream[self.offset as usize]; + self.offset += 1; + result + } +} + +impl PSOCipher for PSOPCCipher { + fn encrypt(&mut self, data: &Vec) -> Result, CipherError> { + let mut result = Vec::new(); + if data.len() % 4 != 0 { + return Err(CipherError::InvalidSize) + } + + for c in data.chunks(4) { + let mut data = u32::from_le_bytes([c[0], c[1], c[2], c[3]]); + data ^= self.next(); + result.extend_from_slice(&u32::to_le_bytes(data)); + } + Ok(result) + } + + fn decrypt(&mut self, data: &Vec) -> Result, CipherError> { + self.encrypt(data) + } +} + + +#[cfg(test)] +mod tests { + #[test] + fn test_crypto() { + use rand::{Rng, RngCore}; + use super::{PSOCipher, PSOPCCipher}; + + let mut rng = rand::thread_rng(); + + let seed: u32 = rng.gen(); + let mut cipher_in = PSOPCCipher::new(seed); + let mut cipher_out = PSOPCCipher::new(seed); + + for _ in 0..10 { + let mut random_junk = vec![0u8; 40]; + rng.fill_bytes(&mut random_junk); + + let enc_data = cipher_in.encrypt(&random_junk).unwrap(); + let orig_data = cipher_out.encrypt(&enc_data).unwrap(); + + assert!(random_junk == orig_data); + } + } +} + + + + + + + + diff --git a/src/lib.rs b/src/lib.rs index 3a99b69..f6a4483 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod crypto; mod patch; #[derive(Debug, PartialEq)]