use bytes::Bytes;
use std::io;
pub mod bytes;
#[derive(Clone, Debug)]
struct XteaKey(Vec<u32>);
impl std::ops::Index<usize> for XteaKey {
type Output = u32;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
#[derive(Debug)]
pub struct Xtea {
key: XteaKey,
rounds: u32,
}
impl Xtea {
const DEFAULT_ROUNDS: u32 = 32;
const DELTA: u32 = 0x9E3779B9;
pub fn using_key(key: [u32; 4]) -> Self {
assert!(key.len() == 4);
Self {
key: XteaKey(key.to_vec()),
rounds: Self::DEFAULT_ROUNDS,
}
}
pub fn with_rounds(mut self, rounds: u32) -> Self {
self.rounds = rounds;
self
}
pub fn encipher(&self, mut input: &mut [u8], mut output: &mut [u8]) -> io::Result<()> {
self.do_block_cipher(&mut input, &mut output, false)
}
pub fn decipher(&self, input: &mut [u8], output: &mut [u8]) -> io::Result<()> {
self.do_block_cipher(input, output, true)
}
fn encipher_block(&self, input: &[u32; 2], output: &mut [u32; 2]) {
let mut v0 = input[0];
let mut v1 = input[1];
let mut sum = 0u32;
for _ in 0..self.rounds {
v0 = v0.wrapping_add(((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
^ (sum.wrapping_add(self.key[(sum & 3) as usize]));
sum = sum.wrapping_add(Self::DELTA);
v1 = v1.wrapping_add(
(((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
^ (sum.wrapping_add(self.key[((sum >> 11) & 3) as usize])),
);
}
output[0] = v0;
output[1] = v1;
}
fn decipher_block(&self, input: &[u32; 2], output: &mut [u32; 2]) {
let mut v0 = input[0];
let mut v1 = input[1];
let mut sum = Self::DELTA.wrapping_mul(self.rounds);
for _ in 0..self.rounds {
v1 = v1.wrapping_sub(
(((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
^ (sum.wrapping_add(self.key[((sum >> 11) & 3) as usize])),
);
sum = sum.wrapping_sub(Self::DELTA);
v0 = v0.wrapping_sub(
(((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
^ (sum.wrapping_add(self.key[(sum & 3) as usize])),
);
}
output[0] = v0;
output[1] = v1;
}
fn do_block_cipher(&self, input: &mut [u8], output: &mut [u8], decrypt: bool) -> io::Result<()> {
let mut input_buffer = Bytes::new(input.to_vec());
let mut output_buffer = Bytes::new(output.to_vec());
let mut input_slice = [0_u32; 2];
let mut output_slice = [0_u32; 2];
let iterations = input_buffer.readable() / 8;
for _ in 0..iterations {
input_slice[0] = input_buffer.get_u32()?;
input_slice[1] = input_buffer.get_u32()?;
if decrypt {
self.decipher_block(&input_slice, &mut output_slice);
} else {
self.encipher_block(&input_slice, &mut output_slice);
}
output_buffer.put_u32(output_slice[0]);
output_buffer.put_u32(output_slice[1]);
}
Ok(())
}
}