lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0
//
// Cryptographically Secure Random Number Generator
// Platform-agnostic with arch-specific entropy sources.

use crate::arch;

/// Error type for random number generation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandomError {
    /// Platform entropy source unavailable
    EntropyUnavailable,
    /// Insufficient entropy
    InsufficientEntropy,
    /// Hardware RNG failure
    HardwareFailure,
}

/// Cryptographically secure random number generator
pub struct CryptoRng {
    /// Entropy pool (256 bits minimum)
    entropy_pool: [u8; 32],
    /// Pool position
    pool_position: usize,
    /// Reseed counter
    reseed_counter: u64,
}

impl CryptoRng {
    /// Create new CSPRNG (initialized with platform entropy)
    pub fn new() -> Result<Self, RandomError> {
        let mut rng = Self {
            entropy_pool: [0u8; 32],
            pool_position: 32, // Force immediate reseed
            reseed_counter: 0,
        };

        rng.reseed()?;
        Ok(rng)
    }

    /// Reseed from platform entropy source
    fn reseed(&mut self) -> Result<(), RandomError> {
        // Try multiple entropy sources in order of preference

        // 1. Try getrandom syscall (Linux only)
        #[cfg(target_os = "linux")]
        {
            if self.getrandom_syscall().is_ok() {
                self.pool_position = 0;
                self.reseed_counter += 1;
                return Ok(());
            }
        }

        // 2. Try platform hardware RNG (RDRAND on x86_64, RNDR on AArch64)
        if arch::has_rdrand() && arch::fill_hardware_entropy(&mut self.entropy_pool).is_ok() {
            self.pool_position = 0;
            self.reseed_counter += 1;
            return Ok(());
        }

        // 3. Fallback: Use hardware timer + mixing (NOT SECURE)
        // This is a last resort and should trigger a warning
        crate::lcpfs_println!("[ RANDOM ] WARNING: Using insecure entropy source!");
        self.timer_entropy_fallback();
        self.pool_position = 0;
        self.reseed_counter += 1;

        Ok(())
    }

    /// Get random bytes (cryptographically secure)
    pub fn fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RandomError> {
        for byte in dest.iter_mut() {
            // Reseed every 1MB or every 256 pool exhaustions
            if self.pool_position >= 32 || self.reseed_counter % 256 == 0 {
                self.reseed()?;
            }

            // XOR with current pool position
            *byte = self.entropy_pool[self.pool_position];
            self.pool_position += 1;
        }

        Ok(())
    }

    /// Generate random u64
    pub fn next_u64(&mut self) -> Result<u64, RandomError> {
        let mut bytes = [0u8; 8];
        self.fill_bytes(&mut bytes)?;
        Ok(u64::from_le_bytes(bytes))
    }

    /// Generate random u32
    pub fn next_u32(&mut self) -> Result<u32, RandomError> {
        let mut bytes = [0u8; 4];
        self.fill_bytes(&mut bytes)?;
        Ok(u32::from_le_bytes(bytes))
    }

    // Platform-specific entropy sources

    #[cfg(target_os = "linux")]
    fn getrandom_syscall(&mut self) -> Result<(), RandomError> {
        // syscall number for getrandom varies by architecture
        #[cfg(target_arch = "x86_64")]
        const SYS_GETRANDOM: i64 = 318;
        #[cfg(target_arch = "aarch64")]
        const SYS_GETRANDOM: i64 = 278;
        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
        const SYS_GETRANDOM: i64 = -1; // Will fail

        const GRND_NONBLOCK: i32 = 0x01;

        if SYS_GETRANDOM < 0 {
            return Err(RandomError::EntropyUnavailable);
        }

        // SAFETY: syscall3 is implemented correctly for each architecture
        // in the arch module. Arguments are valid for getrandom(2):
        // - arg1: valid mutable buffer pointer
        // - arg2: buffer length (32 bytes)
        // - arg3: GRND_NONBLOCK flag
        unsafe {
            let ret = arch::syscall3(
                SYS_GETRANDOM,
                self.entropy_pool.as_mut_ptr() as usize,
                self.entropy_pool.len(),
                GRND_NONBLOCK as usize,
            );

            if ret == self.entropy_pool.len() as i64 {
                Ok(())
            } else {
                Err(RandomError::EntropyUnavailable)
            }
        }
    }

    /// Fallback entropy using timer (INSECURE - for testing only)
    fn timer_entropy_fallback(&mut self) {
        // This is NOT cryptographically secure
        // Use only as absolute last resort

        // Mix timestamp and address space randomization
        let time = arch::get_timestamp();
        let stack_addr = &self.entropy_pool as *const _ as u64;

        let mut seed = time ^ stack_addr;

        for byte in self.entropy_pool.iter_mut() {
            // Very weak PRNG - DO NOT USE IN PRODUCTION
            seed = seed
                .wrapping_mul(6364136223846793005)
                .wrapping_add(1442695040888963407);
            *byte = (seed >> 56) as u8;
        }
    }
}

/// Global CSPRNG instance (lazy initialized)
static mut GLOBAL_RNG: Option<CryptoRng> = None;
static mut RNG_INIT: bool = false;

/// Initialize global RNG
pub fn init_global_rng() -> Result<(), RandomError> {
    // SAFETY INVARIANTS:
    // 1. GLOBAL_RNG and RNG_INIT are global mutable statics
    // 2. Called once during initialization (caller responsibility)
    // 3. RNG_INIT flag prevents double-initialization
    // 4. After init, GLOBAL_RNG only accessed via immutable reference
    // 5. Caller ensures no concurrent access during init
    //
    // JUSTIFICATION:
    // Global RNG required for no_std environment (no thread_local).
    // Mutable static necessary for initialization. Subsequent access
    // via fill_random() is read-only after init flag set.
    unsafe {
        if !RNG_INIT {
            GLOBAL_RNG = Some(CryptoRng::new()?);
            RNG_INIT = true;
        }
        Ok(())
    }
}

/// Fill buffer with cryptographically secure random bytes
pub fn fill_random(buf: &mut [u8]) -> Result<(), RandomError> {
    // SAFETY INVARIANTS:
    // 1. GLOBAL_RNG initialized if RNG_INIT is true
    // 2. Lazy init via init_global_rng() if not yet initialized
    // 3. Mutable reference to GLOBAL_RNG scoped to this block
    // 4. No concurrent modification (caller responsibility)
    // 5. Reference does not escape function
    //
    // JUSTIFICATION:
    // Must access global mutable RNG for random number generation.
    // Lazy initialization allows first-use initialization pattern.
    // Mutable access required for CSPRNG state update.
    unsafe {
        if !RNG_INIT {
            init_global_rng()?;
        }

        if let Some(ref mut rng) = GLOBAL_RNG {
            rng.fill_bytes(buf)
        } else {
            Err(RandomError::EntropyUnavailable)
        }
    }
}

/// Generate random encryption key (32 bytes)
pub fn generate_key() -> Result<[u8; 32], RandomError> {
    let mut key = [0u8; 32];
    fill_random(&mut key)?;
    Ok(key)
}

/// Generate random nonce/IV (16 bytes)
pub fn generate_nonce() -> Result<[u8; 16], RandomError> {
    let mut nonce = [0u8; 16];
    fill_random(&mut nonce)?;
    Ok(nonce)
}

/// Generate random salt (32 bytes)
pub fn generate_salt() -> Result<[u8; 32], RandomError> {
    let mut salt = [0u8; 32];
    fill_random(&mut salt)?;
    Ok(salt)
}

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

    #[test]
    fn test_rng_creation() {
        let rng = CryptoRng::new();
        assert!(rng.is_ok());
    }

    #[test]
    fn test_fill_bytes() {
        let mut rng = CryptoRng::new().expect("test: operation should succeed");
        let mut buf1 = [0u8; 32];
        let mut buf2 = [0u8; 32];

        rng.fill_bytes(&mut buf1)
            .expect("test: operation should succeed");
        rng.fill_bytes(&mut buf2)
            .expect("test: operation should succeed");

        // Should be different
        assert_ne!(buf1, buf2);
    }

    #[test]
    fn test_generate_key() {
        let key1 = generate_key().expect("test: operation should succeed");
        let key2 = generate_key().expect("test: operation should succeed");

        // Should be different
        assert_ne!(key1, key2);
        // Should not be all zeros
        assert_ne!(key1, [0u8; 32]);
    }

    #[test]
    fn test_generate_nonce() {
        let nonce1 = generate_nonce().expect("test: operation should succeed");
        let nonce2 = generate_nonce().expect("test: operation should succeed");

        // Should be different
        assert_ne!(nonce1, nonce2);
    }

    #[test]
    fn test_next_u64() {
        let mut rng = CryptoRng::new().expect("test: operation should succeed");
        let val1 = rng.next_u64().expect("test: operation should succeed");
        let val2 = rng.next_u64().expect("test: operation should succeed");

        // Should be different
        assert_ne!(val1, val2);
    }
}