clock_hash/
clockmix.rs

1//! ClockMix: Message scrambling function for ClockHash-256
2//!
3//! Applies rotation and S-box substitution to message blocks before
4//! injection into the hash state. Uses SIMD acceleration when available.
5
6#[cfg(feature = "simd")]
7use crate::simd::clock_mix_avx2;
8
9use crate::constants::{ROTATION_SCHEDULE, SBOX};
10use crate::utils::rotl64;
11
12/// Apply ClockMix to a 16-word (128-byte) message block.
13///
14/// For each word M[i] (0..15):
15/// 1. M[i] = M[i] ⊕ ROTL(M[(i+1) mod 16], R[i])
16/// 2. M[i] = M[i] + SBOX[M[i] & 0xFF] (wrapping addition)
17///
18/// Uses SIMD acceleration (AVX2) when available and the "simd" feature is enabled.
19///
20/// # Arguments
21///
22/// * `message` - Mutable reference to 16 u64 words representing the message block
23///
24/// # Panics
25///
26/// This function will panic if `message.len() != 16`.
27#[inline]
28pub fn clock_mix(message: &mut [u64; 16]) {
29    #[cfg(feature = "simd")]
30    {
31        clock_mix_avx2(message);
32    }
33
34    #[cfg(not(feature = "simd"))]
35    {
36        // Scalar implementation
37        // Step 1: XOR with rotated neighbor
38        for i in 0..16 {
39            let next_idx = (i + 1) % 16;
40            let rotation = ROTATION_SCHEDULE[i];
41            message[i] ^= rotl64(message[next_idx], rotation);
42        }
43
44        // Step 2: Add S-box value
45        for i in 0..16 {
46            let sbox_idx = (message[i] & 0xFF) as usize;
47            message[i] = message[i].wrapping_add(SBOX[sbox_idx] as u64);
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_clock_mix_basic() {
58        let mut msg = [0u64; 16];
59        msg[0] = 0x1234567890ABCDEF;
60
61        let original = msg;
62        clock_mix(&mut msg);
63
64        // ClockMix should modify the message
65        assert_ne!(msg, original);
66    }
67
68    #[test]
69    fn test_clock_mix_avalanche() {
70        let mut msg1 = [0u64; 16];
71        let mut msg2 = [0u64; 16];
72
73        msg1[0] = 0x0000000000000000;
74        msg2[0] = 0x0000000000000001; // Single bit difference
75
76        clock_mix(&mut msg1);
77        clock_mix(&mut msg2);
78
79        // Should produce different results (avalanche effect)
80        assert_ne!(msg1, msg2);
81    }
82
83    #[test]
84    fn test_clock_mix_deterministic() {
85        let mut msg = [0u64; 16];
86        msg[0] = 0xDEADBEEFCAFEBABE;
87
88        let mut msg_copy = msg;
89
90        clock_mix(&mut msg);
91        clock_mix(&mut msg_copy);
92
93        // Same input should produce same output
94        assert_eq!(msg, msg_copy);
95    }
96}