clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! ClockMix: Message scrambling function for ClockHash-256
//!
//! Applies rotation and S-box substitution to message blocks before
//! injection into the hash state. Uses SIMD acceleration when available.

#[cfg(feature = "simd")]
use crate::simd::clock_mix_avx2;

use crate::constants::{ROTATION_SCHEDULE, SBOX};
use crate::utils::rotl64;

/// Apply ClockMix to a 16-word (128-byte) message block.
///
/// For each word M[i] (0..15):
/// 1. M[i] = M[i] ⊕ ROTL(M[(i+1) mod 16], R[i])
/// 2. M[i] = M[i] + SBOX[M[i] & 0xFF] (wrapping addition)
///
/// Uses SIMD acceleration (AVX2) when available and the "simd" feature is enabled.
///
/// # Arguments
///
/// * `message` - Mutable reference to 16 u64 words representing the message block
///
/// # Panics
///
/// This function will panic if `message.len() != 16`.
#[inline]
pub fn clock_mix(message: &mut [u64; 16]) {
    #[cfg(feature = "simd")]
    {
        clock_mix_avx2(message);
    }

    #[cfg(not(feature = "simd"))]
    {
        // Scalar implementation
        // Step 1: XOR with rotated neighbor
        for i in 0..16 {
            let next_idx = (i + 1) % 16;
            let rotation = ROTATION_SCHEDULE[i];
            message[i] ^= rotl64(message[next_idx], rotation);
        }

        // Step 2: Add S-box value
        for i in 0..16 {
            let sbox_idx = (message[i] & 0xFF) as usize;
            message[i] = message[i].wrapping_add(SBOX[sbox_idx] as u64);
        }
    }
}

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

    #[test]
    fn test_clock_mix_basic() {
        let mut msg = [0u64; 16];
        msg[0] = 0x1234567890ABCDEF;

        let original = msg;
        clock_mix(&mut msg);

        // ClockMix should modify the message
        assert_ne!(msg, original);
    }

    #[test]
    fn test_clock_mix_avalanche() {
        let mut msg1 = [0u64; 16];
        let mut msg2 = [0u64; 16];

        msg1[0] = 0x0000000000000000;
        msg2[0] = 0x0000000000000001; // Single bit difference

        clock_mix(&mut msg1);
        clock_mix(&mut msg2);

        // Should produce different results (avalanche effect)
        assert_ne!(msg1, msg2);
    }

    #[test]
    fn test_clock_mix_deterministic() {
        let mut msg = [0u64; 16];
        msg[0] = 0xDEADBEEFCAFEBABE;

        let mut msg_copy = msg;

        clock_mix(&mut msg);
        clock_mix(&mut msg_copy);

        // Same input should produce same output
        assert_eq!(msg, msg_copy);
    }
}