rs-crypto 0.1.2

Cryptographic Primitives: ISAAC, RSA, XTEA, Huffman, Whirlpool
Documentation
use std::fmt::Debug;
use std::os::raw::c_int;

const RANDSIZ: usize = 256;

#[repr(C)]
struct RandCtx {
    randcnt: u32,
    randrsl: [u32; RANDSIZ],
    randmem: [u32; RANDSIZ],
    randa: u32,
    randb: u32,
    randc: u32,
}

unsafe extern "C" {
    fn randinit(ctx: *mut RandCtx, flag: c_int);
    fn isaac(ctx: *mut RandCtx);
}

#[derive(Clone)]
pub struct Isaac {
    ctx: RandCtx,
}

impl Debug for Isaac {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Isaac").finish_non_exhaustive()
    }
}

impl Clone for RandCtx {
    fn clone(&self) -> Self {
        Self {
            randcnt: self.randcnt,
            randrsl: self.randrsl,
            randmem: self.randmem,
            randa: self.randa,
            randb: self.randb,
            randc: self.randc,
        }
    }
}

impl Isaac {
    pub fn new(seed: &[u32; 4]) -> Self {
        let mut ctx = RandCtx {
            randcnt: 0,
            randrsl: [0; RANDSIZ],
            randmem: [0; RANDSIZ],
            randa: 0,
            randb: 0,
            randc: 0,
        };

        ctx.randrsl[..4].copy_from_slice(seed);

        unsafe {
            randinit(&mut ctx, 1);
        }

        Self { ctx }
    }

    pub fn next_int(&mut self) -> u32 {
        if self.ctx.randcnt == 0 {
            unsafe {
                isaac(&mut self.ctx);
            }
            self.ctx.randcnt = RANDSIZ as u32;
        }
        self.ctx.randcnt -= 1;
        self.ctx.randrsl[self.ctx.randcnt as usize]
    }

    pub fn next_int_max(&mut self, max: u32) -> u32 {
        if max == 0 {
            return 0;
        }
        self.next_int() % max
    }

    pub fn next_int_range(&mut self, min: i32, max: i32) -> i32 {
        min + (self.next_int() % (max - min) as u32) as i32
    }
}

#[derive(Debug, Clone)]
pub struct IsaacPair {
    pub decode: Isaac,
    pub encode: Isaac,
}

impl IsaacPair {
    pub fn new(decode_seed: &[u32; 4], encode_seed: &[u32; 4]) -> Self {
        Self {
            decode: Isaac::new(decode_seed),
            encode: Isaac::new(encode_seed),
        }
    }

    pub fn from_client_seeds(seeds: &[i32; 4]) -> Self {
        let decode_seed: [u32; 4] = [
            seeds[0] as u32,
            seeds[1] as u32,
            seeds[2] as u32,
            seeds[3] as u32,
        ];
        let encode_seed: [u32; 4] = [
            (seeds[0] + 50) as u32,
            (seeds[1] + 50) as u32,
            (seeds[2] + 50) as u32,
            (seeds[3] + 50) as u32,
        ];
        Self {
            decode: Isaac::new(&decode_seed),
            encode: Isaac::new(&encode_seed),
        }
    }

    pub fn from_session_key(session_key: u64) -> ([u32; 4], [u32; 4]) {
        let key_low = session_key as u32;
        let key_high = (session_key >> 32) as u32;

        let decode_seed = [
            key_low,
            key_high,
            key_low.wrapping_add(1),
            key_high.wrapping_add(1),
        ];
        let encode_seed = [
            key_low ^ 0xDEADBEEF,
            key_high ^ 0xCAFEBABE,
            key_low.wrapping_sub(1),
            key_high.wrapping_sub(1),
        ];

        (decode_seed, encode_seed)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_isaac_deterministic() {
        let seed = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210];

        let mut isaac1 = Isaac::new(&seed);
        let mut isaac2 = Isaac::new(&seed);

        for _ in 0..1000 {
            assert_eq!(isaac1.next_int(), isaac2.next_int());
        }
    }

    #[test]
    fn test_isaac_different_values() {
        let seed = [0x12345678, 0x9ABCDEF0, 0xFEDCBA98, 0x76543210];
        let mut isaac = Isaac::new(&seed);

        let v1 = isaac.next_int();
        let v2 = isaac.next_int();
        let v3 = isaac.next_int();

        assert_ne!(v1, v2);
        assert_ne!(v2, v3);
    }

    #[test]
    fn test_isaac_pair() {
        let session_key = 0x0123456789ABCDEF;
        let (decode_seed, encode_seed) = IsaacPair::from_session_key(session_key);
        let _pair = IsaacPair::new(&decode_seed, &encode_seed);
    }

    #[test]
    fn test_different_seeds_produce_different_sequences() {
        let mut a = Isaac::new(&[1, 2, 3, 4]);
        let mut b = Isaac::new(&[5, 6, 7, 8]);

        let seq_a: Vec<u32> = (0..10).map(|_| a.next_int()).collect();
        let seq_b: Vec<u32> = (0..10).map(|_| b.next_int()).collect();
        assert_ne!(seq_a, seq_b);
    }

    #[test]
    fn test_zero_seed() {
        let mut isaac = Isaac::new(&[0, 0, 0, 0]);
        let v1 = isaac.next_int();
        let v2 = isaac.next_int();
        assert_ne!(v1, v2);
    }

    #[test]
    fn test_next_int_max_zero() {
        let mut isaac = Isaac::new(&[1, 2, 3, 4]);
        assert_eq!(isaac.next_int_max(0), 0);
    }

    #[test]
    fn test_next_int_max_bounds() {
        let mut isaac = Isaac::new(&[1, 2, 3, 4]);
        for _ in 0..1000 {
            assert!(isaac.next_int_max(100) < 100);
        }
    }

    #[test]
    fn test_next_int_range_bounds() {
        let mut isaac = Isaac::new(&[1, 2, 3, 4]);
        for _ in 0..1000 {
            let v = isaac.next_int_range(10, 20);
            assert!((10..20).contains(&v));
        }
    }

    #[test]
    fn test_clone_produces_independent_state() {
        let mut original = Isaac::new(&[1, 2, 3, 4]);
        for _ in 0..10 {
            original.next_int();
        }

        let mut cloned = original.clone();
        assert_eq!(original.next_int(), cloned.next_int());

        original.next_int();
        let v_orig = original.next_int();
        let v_clone = cloned.next_int();
        assert_ne!(v_orig, v_clone);
    }

    #[test]
    fn test_exhausts_multiple_batches() {
        let mut isaac = Isaac::new(&[1, 2, 3, 4]);
        let mut values = std::collections::HashSet::new();
        for _ in 0..1000 {
            values.insert(isaac.next_int());
        }
        assert!(values.len() > 900);
    }

    #[test]
    fn test_from_client_seeds() {
        let client_seeds: [i32; 4] = [100, 200, 300, 400];
        let pair = IsaacPair::from_client_seeds(&client_seeds);

        let mut decode_check = Isaac::new(&[100, 200, 300, 400]);
        let mut encode_check = Isaac::new(&[150, 250, 350, 450]);

        assert_eq!(pair.decode.clone().next_int(), decode_check.next_int());
        assert_eq!(pair.encode.clone().next_int(), encode_check.next_int());
    }

    #[test]
    fn test_from_session_key_values() {
        let session_key: u64 = 0x0123456789ABCDEF;
        let key_low = session_key as u32;
        let key_high = (session_key >> 32) as u32;

        let (decode, encode) = IsaacPair::from_session_key(session_key);

        assert_eq!(decode[0], key_low);
        assert_eq!(decode[1], key_high);
        assert_eq!(decode[2], key_low.wrapping_add(1));
        assert_eq!(decode[3], key_high.wrapping_add(1));

        assert_eq!(encode[0], key_low ^ 0xDEADBEEF);
        assert_eq!(encode[1], key_high ^ 0xCAFEBABE);
        assert_eq!(encode[2], key_low.wrapping_sub(1));
        assert_eq!(encode[3], key_high.wrapping_sub(1));
    }

    #[test]
    fn test_decode_encode_are_independent() {
        let pair = IsaacPair::new(&[1, 2, 3, 4], &[5, 6, 7, 8]);
        let mut decode = pair.decode;
        let mut encode = pair.encode;

        let decode_vals: Vec<u32> = (0..10).map(|_| decode.next_int()).collect();
        let encode_vals: Vec<u32> = (0..10).map(|_| encode.next_int()).collect();
        assert_ne!(decode_vals, encode_vals);
    }
}