Jake Probst
6 years ago
5 changed files with 164 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||||
|
mod pc;
|
||||
|
|
||||
|
|
||||
|
#[derive(Debug)]
|
||||
|
pub enum CipherError {
|
||||
|
InvalidSize
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
trait PSOCipher {
|
||||
|
fn encrypt(&mut self, data: &Vec<u8>) -> Result<Vec<u8>, CipherError>;
|
||||
|
fn decrypt(&mut self, data: &Vec<u8>) -> Result<Vec<u8>, CipherError>;
|
||||
|
}
|
@ -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<u32>;
|
||||
|
let mut ebx: W<u32>;
|
||||
|
let mut edi: W<u32>;
|
||||
|
let mut eax: W<u32>;
|
||||
|
let mut edx: W<u32>;
|
||||
|
let mut var1: W<u32>;
|
||||
|
|
||||
|
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<u32>;
|
||||
|
let mut edi: W<u32>;
|
||||
|
let mut eax: W<u32>;
|
||||
|
let mut ebp: W<u32>;
|
||||
|
let mut edx: W<u32>;
|
||||
|
|
||||
|
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<u8>) -> Result<Vec<u8>, 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<u8>) -> Result<Vec<u8>, 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);
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue