use crate::opcodes::{stack, arithmetic};
#[derive(Clone, Copy, Debug)]
pub enum CryptCommand {
Add(u64),
Sub(u64),
Xor(u64),
Rol(u32),
Ror(u32),
Not,
Neg,
}
impl CryptCommand {
pub fn encrypt(&self, value: u64) -> u64 {
match self {
CryptCommand::Add(c) => value.wrapping_add(*c),
CryptCommand::Sub(c) => value.wrapping_sub(*c),
CryptCommand::Xor(c) => value ^ c,
CryptCommand::Rol(n) => value.rotate_left(*n),
CryptCommand::Ror(n) => value.rotate_right(*n),
CryptCommand::Not => !value,
CryptCommand::Neg => (value as i64).wrapping_neg() as u64,
}
}
pub fn decrypt(&self, value: u64) -> u64 {
match self {
CryptCommand::Add(c) => value.wrapping_sub(*c), CryptCommand::Sub(c) => value.wrapping_add(*c), CryptCommand::Xor(c) => value ^ c, CryptCommand::Rol(n) => value.rotate_right(*n), CryptCommand::Ror(n) => value.rotate_left(*n), CryptCommand::Not => !value, CryptCommand::Neg => (value as i64).wrapping_neg() as u64, }
}
pub fn emit_decrypt<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) {
match self {
CryptCommand::Add(c) => {
emit_push_value(bytecode, *c, encode);
bytecode.push(encode(arithmetic::SUB));
}
CryptCommand::Sub(c) => {
emit_push_value(bytecode, *c, encode);
bytecode.push(encode(arithmetic::ADD));
}
CryptCommand::Xor(c) => {
emit_push_value(bytecode, *c, encode);
bytecode.push(encode(arithmetic::XOR));
}
CryptCommand::Rol(n) => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(*n as u8);
bytecode.push(encode(arithmetic::ROR));
}
CryptCommand::Ror(n) => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(*n as u8);
bytecode.push(encode(arithmetic::ROL));
}
CryptCommand::Not => {
bytecode.push(encode(arithmetic::NOT));
}
CryptCommand::Neg => {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(0);
bytecode.push(encode(stack::SWAP));
bytecode.push(encode(arithmetic::SUB));
}
}
}
}
fn emit_push_value<F: Fn(u8) -> u8>(bytecode: &mut Vec<u8>, value: u64, encode: &F) {
if value <= 0xFF {
bytecode.push(encode(stack::PUSH_IMM8));
bytecode.push(value as u8);
} else if value <= 0xFFFF {
bytecode.push(encode(stack::PUSH_IMM16));
bytecode.extend_from_slice(&(value as u16).to_le_bytes());
} else if value <= 0xFFFFFFFF {
bytecode.push(encode(stack::PUSH_IMM32));
bytecode.extend_from_slice(&(value as u32).to_le_bytes());
} else {
bytecode.push(encode(stack::PUSH_IMM));
bytecode.extend_from_slice(&value.to_le_bytes());
}
}
pub struct ValueCryptor {
commands: Vec<CryptCommand>,
rng: u64,
}
impl ValueCryptor {
pub fn new(seed: u64) -> Self {
Self {
commands: Vec::new(),
rng: seed ^ 0x9E3779B97F4A7C15, }
}
fn next_rand(&mut self) -> u64 {
self.rng ^= self.rng >> 12;
self.rng ^= self.rng << 25;
self.rng ^= self.rng >> 27;
self.rng.wrapping_mul(0x2545F4914F6CDD1D)
}
pub fn generate_chain(&mut self) -> &[CryptCommand] {
self.commands.clear();
let count = 3 + (self.next_rand() % 5) as usize;
let mut last_cmd_type = 255u8;
for _ in 0..count {
let cmd = loop {
let cmd_type = (self.next_rand() % 7) as u8;
if cmd_type == last_cmd_type {
continue;
}
if (cmd_type == 0 || cmd_type == 1) && (last_cmd_type == 0 || last_cmd_type == 1) {
continue;
}
if (cmd_type == 3 || cmd_type == 4) && (last_cmd_type == 3 || last_cmd_type == 4) {
continue;
}
last_cmd_type = cmd_type;
break match cmd_type {
0 => CryptCommand::Add(self.next_rand()),
1 => CryptCommand::Sub(self.next_rand()),
2 => CryptCommand::Xor(self.next_rand()),
3 => CryptCommand::Rol(1 + (self.next_rand() % 63) as u32),
4 => CryptCommand::Ror(1 + (self.next_rand() % 63) as u32),
5 => CryptCommand::Not,
_ => CryptCommand::Neg,
};
};
self.commands.push(cmd);
}
&self.commands
}
pub fn encrypt(&self, mut value: u64) -> u64 {
for cmd in &self.commands {
value = cmd.encrypt(value);
}
value
}
#[allow(dead_code)]
pub fn decrypt(&self, mut value: u64) -> u64 {
for cmd in self.commands.iter().rev() {
value = cmd.decrypt(value);
}
value
}
pub fn emit_decrypt_chain<F: Fn(u8) -> u8>(&self, bytecode: &mut Vec<u8>, encode: &F) {
for cmd in self.commands.iter().rev() {
cmd.emit_decrypt(bytecode, encode);
}
}
pub fn emit_encrypted_value<F: Fn(u8) -> u8>(
&mut self,
value: u64,
bytecode: &mut Vec<u8>,
encode: &F,
) {
self.generate_chain();
let encrypted = self.encrypt(value);
emit_push_value(bytecode, encrypted, encode);
self.emit_decrypt_chain(bytecode, encode);
}
#[allow(dead_code)]
pub fn command_count(&self) -> usize {
self.commands.len()
}
}
#[allow(dead_code)]
pub struct LightValueCryptor {
rng: u64,
}
#[allow(dead_code)]
impl LightValueCryptor {
pub fn new(seed: u64) -> Self {
Self {
rng: seed ^ 0xDEADBEEF_CAFEBABE,
}
}
fn next_rand(&mut self) -> u64 {
self.rng = self.rng.wrapping_mul(0x5851F42D4C957F2D).wrapping_add(0x14057B7EF767814F);
self.rng
}
pub fn emit_light<F: Fn(u8) -> u8>(
&mut self,
value: u64,
bytecode: &mut Vec<u8>,
encode: &F,
) {
if self.next_rand().is_multiple_of(2) {
let key = self.next_rand();
let encrypted = value ^ key;
emit_push_value(bytecode, encrypted, encode);
emit_push_value(bytecode, key, encode);
bytecode.push(encode(arithmetic::XOR));
} else {
let a = self.next_rand();
let b = value.wrapping_sub(a);
emit_push_value(bytecode, a, encode);
emit_push_value(bytecode, b, encode);
bytecode.push(encode(arithmetic::ADD));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_roundtrip() {
let mut cryptor = ValueCryptor::new(12345);
for value in [0u64, 1, 42, 0xDEADBEEF, u64::MAX, 0x123456789ABCDEF0] {
cryptor.generate_chain();
let encrypted = cryptor.encrypt(value);
let decrypted = cryptor.decrypt(encrypted);
assert_eq!(value, decrypted, "Roundtrip failed for {}", value);
}
}
#[test]
fn test_chain_generation_deterministic() {
let mut cryptor1 = ValueCryptor::new(42);
let mut cryptor2 = ValueCryptor::new(42);
cryptor1.generate_chain();
cryptor2.generate_chain();
assert_eq!(cryptor1.commands.len(), cryptor2.commands.len());
let val = 0xCAFEBABE;
assert_eq!(cryptor1.encrypt(val), cryptor2.encrypt(val));
}
#[test]
fn test_different_seeds_different_chains() {
let mut cryptor1 = ValueCryptor::new(1);
let mut cryptor2 = ValueCryptor::new(2);
cryptor1.generate_chain();
cryptor2.generate_chain();
let val = 0xDEADBEEF;
assert_ne!(cryptor1.encrypt(val), cryptor2.encrypt(val));
}
#[test]
fn test_individual_commands() {
let value = 0x123456789ABCDEF0u64;
let cmd = CryptCommand::Add(100);
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Sub(100);
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Xor(0xFFFF);
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Rol(13);
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Ror(27);
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Not;
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
let cmd = CryptCommand::Neg;
assert_eq!(cmd.decrypt(cmd.encrypt(value)), value);
}
}