pub const EEXEC_SEED: u16 = 55665;
pub const CHARSTRING_SEED: u16 = 4330;
pub const EEXEC_SKIP: usize = 4;
pub const DEFAULT_LEN_IV: i32 = 4;
const MULT: u16 = 52845;
const ADD: u16 = 22719;
#[must_use]
pub fn decrypt(cipher: &[u8], seed: u16, skip: usize) -> Vec<u8> {
let mut r = seed;
let mut out = Vec::with_capacity(cipher.len().saturating_sub(skip));
for (i, &c) in cipher.iter().enumerate() {
let plain = c ^ (r >> 8) as u8;
r = (u16::from(c).wrapping_add(r))
.wrapping_mul(MULT)
.wrapping_add(ADD);
if i >= skip {
out.push(plain);
}
}
out
}
#[must_use]
pub fn encrypt(plain: &[u8], seed: u16) -> Vec<u8> {
let mut r = seed;
let mut out = Vec::with_capacity(plain.len());
for &p in plain {
let c = p ^ (r >> 8) as u8;
out.push(c);
r = (u16::from(c).wrapping_add(r))
.wrapping_mul(MULT)
.wrapping_add(ADD);
}
out
}
#[must_use]
pub fn len_iv_skip(len_iv: i32) -> usize {
usize::try_from(len_iv).unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::{CHARSTRING_SEED, DEFAULT_LEN_IV, EEXEC_SEED, EEXEC_SKIP, decrypt, encrypt};
#[test]
fn known_eexec_vector() {
let cipher = encrypt(&[0u8; 32], EEXEC_SEED);
let mut r: u16 = EEXEC_SEED;
let expected: Vec<u8> = (0..32)
.map(|_| {
let c = (r >> 8) as u8; r = (u16::from(c).wrapping_add(r))
.wrapping_mul(52845)
.wrapping_add(22719);
c
})
.collect();
assert_eq!(cipher, expected);
assert_eq!(cipher.len(), 32);
assert_eq!(decrypt(&cipher, EEXEC_SEED, EEXEC_SKIP), vec![0u8; 28]);
}
#[test]
fn len_iv_of_zero_four_and_eight() {
let plain: Vec<u8> = (0u8..24).collect();
let cipher = encrypt(&plain, CHARSTRING_SEED);
for skip in [0usize, DEFAULT_LEN_IV as usize, 8] {
assert_eq!(
decrypt(&cipher, CHARSTRING_SEED, skip),
plain.get(skip..).unwrap_or_default(),
"lenIV {skip}"
);
}
}
#[test]
fn skip_past_the_end_is_empty_not_a_panic() {
let cipher = encrypt(b"abc", CHARSTRING_SEED);
assert!(decrypt(&cipher, CHARSTRING_SEED, 99).is_empty());
}
#[test]
fn negative_len_iv_discards_nothing() {
assert_eq!(super::len_iv_skip(-1), 0);
assert_eq!(super::len_iv_skip(4), 4);
}
}