clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Security verification utilities for ClockHash-256
//!
//! This module provides tools for verifying the security properties of ClockHash-256,
//! including constant-time operation verification and side-channel resistance checks.

/// Verify that ClockHash-256 operations are constant-time
///
/// This function performs basic constant-time verification by ensuring that
/// operations complete in predictable time regardless of input data.
/// Note: This is a basic verification; for full constant-time guarantees,
/// use formal verification tools like ctgrind or valgrind.
///
/// # Arguments
///
/// * `iterations` - Number of iterations to test for timing consistency
///
/// # Returns
///
/// Returns `true` if the operations appear constant-time within the test parameters
#[cfg(feature = "std")]
pub fn verify_constant_time(iterations: usize) -> bool {
    use crate::clockhash256;
    use std::time::Instant;

    let mut times = Vec::with_capacity(iterations);

    // Measure timing for different inputs
    for i in 0..iterations {
        let input = if i % 2 == 0 { [0u8; 64] } else { [0xFFu8; 64] };
        let start = Instant::now();
        let _hash = clockhash256(&input);
        let elapsed = start.elapsed();
        times.push(elapsed.as_nanos());
    }

    // Check that timing variation is within acceptable bounds
    // Allow 10% variation to account for system noise
    if let (Some(&min), Some(&max)) = (times.iter().min(), times.iter().max()) {
        let variation = (max - min) as f64 / min as f64;
        variation < 0.1 // Less than 10% variation
    } else {
        false
    }
}

/// Verify avalanche effect for security
///
/// Ensures that small input changes result in large output changes.
/// This is a fundamental property of secure hash functions.
///
/// # Arguments
///
/// * `input1` - First input data
/// * `input2` - Second input data (differing by 1 bit)
///
/// # Returns
///
/// Returns the avalanche coefficient (0.0 to 1.0), where 0.5 is ideal
pub fn verify_avalanche(input1: &[u8], input2: &[u8]) -> f64 {
    use crate::clockhash256;

    let hash1 = clockhash256(input1);
    let hash2 = clockhash256(input2);

    // Count differing bits
    let mut diff_bits = 0;
    for i in 0..32 {
        diff_bits += (hash1[i] ^ hash2[i]).count_ones() as usize;
    }

    // Avalanche coefficient: fraction of output bits that changed
    diff_bits as f64 / 256.0
}

/// Verify collision resistance properties
///
/// Performs basic collision resistance testing by hashing different inputs
/// and ensuring no accidental collisions occur.
///
/// # Arguments
///
/// * `test_cases` - Array of test input data
///
/// # Returns
///
/// Returns `true` if no collisions were found among the test cases
pub fn verify_collision_resistance(test_cases: &[&[u8]]) -> bool {
    use crate::clockhash256;

    // Simple collision check without HashSet (works in no_std)
    for i in 0..test_cases.len() {
        let hash_i = clockhash256(test_cases[i]);
        for j in (i + 1)..test_cases.len() {
            let hash_j = clockhash256(test_cases[j]);
            if hash_i == hash_j {
                return false; // Collision found
            }
        }
    }

    true // No collisions found
}

/// Verify domain separation properties
///
/// Ensures that domain-separated hashes are different even when the
/// underlying data is identical.
///
/// # Arguments
///
/// * `data` - Test data to hash
/// * `domains` - Array of domain identifiers to test
///
/// # Returns
///
/// Returns `true` if all domain-separated hashes are unique
pub fn verify_domain_separation(data: &[u8], domains: &[&[u8]]) -> bool {
    use crate::clockhash256_domain;

    // Simple uniqueness check without HashSet (works in no_std)
    for i in 0..domains.len() {
        let hash_i = clockhash256_domain(domains[i], data);
        for j in (i + 1)..domains.len() {
            let hash_j = clockhash256_domain(domains[j], data);
            if hash_i == hash_j {
                return false; // Domain separation failed
            }
        }
    }

    true // All domains produce unique hashes
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{clockhash256, tags};

    #[test]
    fn test_avalanche_effect() {
        let input1 = [0u8; 64];
        let mut input2 = input1;
        input2[0] ^= 1; // Flip one bit

        let avalanche = verify_avalanche(&input1, &input2);

        // Avalanche coefficient should be close to 0.5 (50% of bits change)
        assert!(
            avalanche > 0.3 && avalanche < 0.7,
            "Avalanche coefficient {} should be close to 0.5",
            avalanche
        );
    }

    #[test]
    fn test_collision_resistance() {
        let test_cases: &[&[u8]] = &[
            b"",
            b"a",
            b"abc",
            b"message digest",
            b"abcdefghijklmnopqrstuvwxyz",
            &[0u8; 64],
            &[0xFFu8; 64],
        ];

        assert!(
            verify_collision_resistance(test_cases),
            "Collision resistance test failed"
        );
    }

    #[test]
    fn test_domain_separation() {
        let data = b"test data";
        let domains = [
            tags::CLK_BLOCK,
            tags::CLK_TX,
            tags::CLK_MERKLE,
            tags::CLK_NONCE,
            tags::CLK_RNG,
        ];

        assert!(
            verify_domain_separation(data, &domains),
            "Domain separation test failed"
        );
    }

    #[test]
    #[cfg(feature = "std")]
    fn test_constant_time() {
        // Note: This is a basic timing test and may be affected by system noise
        // For proper constant-time verification, use specialized tools like ctgrind
        let is_constant_time = verify_constant_time(100);

        // In a noise-free environment, this should pass
        // In practice, system noise may cause false failures
        if !is_constant_time {
            println!("Warning: Constant-time test failed - may be due to system noise");
            println!("For proper constant-time verification, use ctgrind or valgrind");
        }
    }

    #[test]
    fn test_preimage_resistance() {
        // Basic preimage resistance test
        let target_hash = clockhash256(b"known input");
        let different_input = b"different input";
        let different_hash = clockhash256(different_input);

        assert_ne!(
            target_hash, different_hash,
            "Different inputs should produce different hashes"
        );
    }

    #[test]
    fn test_second_preimage_resistance() {
        // Basic second preimage resistance test
        let original_data = b"original message";
        let original_hash = clockhash256(original_data);

        // Try to find different data with same hash (should fail)
        let test_cases: &[&[u8]] = &[
            b"different message",
            b"another message",
            b"modified message",
        ];

        for &test_case in test_cases {
            let test_hash = clockhash256(test_case);
            assert_ne!(
                original_hash, test_hash,
                "Second preimage resistance: different data should not collide"
            );
        }
    }
}