pub mod prng;
use crate::primitives::prp::prng::Aes128Prng;
use crate::primitives::{Prp, PrpError, PrpResult};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Zeroize)]
pub struct KnuthShufflePRP<T: Zeroize, const N: usize> {
permutation: [T; N],
inverse: [T; N],
}
impl<T: Zeroize, const N: usize> Drop for KnuthShufflePRP<T, N> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<T: Zeroize, const N: usize> ZeroizeOnDrop for KnuthShufflePRP<T, N> {}
impl Prp<u8> for KnuthShufflePRP<u8, 256> {
fn new(key: &[u8]) -> PrpResult<Self> {
let mut rng = Aes128Prng::init(key);
let mut perm = Self {
permutation: [0u8; 256],
inverse: [0u8; 256],
};
for i in 0..=255 {
perm.permutation[i] = i as u8;
}
(0..=255usize).rev().for_each(|i| {
let j = rng.gen_range(i as u8);
perm.permutation.swap(i, j as usize);
});
for (index, val) in perm.permutation.iter().enumerate() {
perm.inverse[*val as usize] = index as u8;
}
Ok(perm)
}
fn permute(&self, input: u8) -> PrpResult<u8> {
let index = usize::from(input);
match self.inverse.get(index) {
Some(i) => Ok(*i),
None => Err(PrpError),
}
}
fn invert(&self, input: u8) -> PrpResult<u8> {
let index = usize::from(input);
match self.permutation.get(index) {
Some(i) => Ok(*i),
None => Err(PrpError),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
fn init_prp() -> PrpResult<KnuthShufflePRP<u8, 256>> {
let key: [u8; 16] = hex!("00010203 04050607 08090a0b 0c0d0eaa");
Prp::new(&key)
}
#[test]
fn test_invert() -> Result<(), PrpError> {
let prp = init_prp()?;
for i in 0..=255 {
assert_eq!(
i,
prp.invert(prp.permute(i)?)?,
"permutation round-trip failed"
);
}
Ok(())
}
}