clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Incremental hasher for ClockHash-256
//!
//! Provides a streaming interface for hashing data incrementally.

use crate::constants::IV;
use crate::padding::{BLOCK_SIZE, pad_message_in_place, MAX_PADDED_SIZE};

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

#[cfg(not(feature = "simd"))]
use crate::clockmix::clock_mix;

#[cfg(not(feature = "simd"))]
use crate::clockpermute::clock_permute;

#[cfg(not(feature = "simd"))]
use crate::utils::rotr64;

/// Incremental hasher for ClockHash-256
///
/// `ClockHasher` provides a streaming interface for computing ClockHash-256 hashes
/// incrementally. This is useful when the input data is too large to fit in memory
/// at once, or when data arrives in chunks (e.g., network streams, file reading).
///
/// The hasher maintains internal state and buffers partial blocks until enough
/// data is available for processing. It automatically handles padding and finalization.
///
/// # Examples
///
/// Basic incremental hashing:
/// ```rust
/// use clock_hash::ClockHasher;
///
/// let mut hasher = ClockHasher::new();
/// hasher.update(b"Hello");
/// hasher.update(b", ");
/// hasher.update(b"World!");
/// let hash = hasher.finalize();
/// assert_eq!(hash.len(), 32);
/// ```
///
/// Incremental hashing produces the same result as one-shot hashing:
/// ```rust
/// # use clock_hash::{ClockHasher, clockhash256};
/// let data = b"The quick brown fox jumps over the lazy dog";
///
/// // Incremental
/// let mut hasher = ClockHasher::new();
/// hasher.update(data);
/// let incremental_hash = hasher.finalize();
///
/// // One-shot
/// let oneshot_hash = clockhash256(data);
///
/// assert_eq!(incremental_hash, oneshot_hash);
/// ```
///
/// Hashing large files or streams:
/// ```rust,no_run
/// # use clock_hash::ClockHasher;
/// # use std::fs::File;
/// # use std::io::Read;
/// let mut hasher = ClockHasher::new();
/// let mut file = File::open("large_file.dat")?;
/// let mut buffer = [0u8; 8192]; // 8KB chunks
///
/// while let Ok(n) = file.read(&mut buffer) {
///     if n == 0 { break; }
///     hasher.update(&buffer[..n]);
/// }
///
/// let file_hash = hasher.finalize();
/// # Ok::<(), std::io::Error>(())
/// ```
pub struct ClockHasher {
    /// Internal state (8 u64 words) initialized from IV constants
    state: [u64; 8],
    /// Buffer for partial blocks (max 127 bytes before processing)
    buffer: [u8; BLOCK_SIZE],
    /// Number of bytes currently in buffer
    buffer_len: usize,
    /// Total message length in bytes (used for padding)
    message_len: u64,
}

impl ClockHasher {
    /// Create a new ClockHasher with initial state from IV.
    ///
    /// The hasher is initialized with the ClockHash-256 initialization vector
    /// and is ready to accept data via `update()`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use clock_hash::ClockHasher;
    ///
    /// let hasher = ClockHasher::new();
    /// // Now you can call hasher.update(data) and hasher.finalize()
    /// ```
    #[inline]
    pub fn new() -> Self {
        Self {
            state: IV,
            buffer: [0u8; BLOCK_SIZE],
            buffer_len: 0,
            message_len: 0,
        }
    }

    /// Process a complete 128-byte block.
    ///
    /// Uses SIMD acceleration when available and the "simd" feature is enabled.
    ///
    /// # Arguments
    ///
    /// * `block` - A 128-byte block (must be exactly BLOCK_SIZE bytes)
    #[inline]
    fn process_block(&mut self, block: &[u8; BLOCK_SIZE]) {
        #[cfg(feature = "simd")]
        {
            process_block_simd(block, &mut self.state);
        }

        #[cfg(not(feature = "simd"))]
        {
            // Parse block to 16 u64 words (little-endian)
            let mut words = [0u64; 16];
            for i in 0..16 {
                let offset = i * 8;
                words[i] = u64::from_le_bytes([
                    block[offset],
                    block[offset + 1],
                    block[offset + 2],
                    block[offset + 3],
                    block[offset + 4],
                    block[offset + 5],
                    block[offset + 6],
                    block[offset + 7],
                ]);
            }

            // Apply ClockMix
            clock_mix(&mut words);

            // Inject into state
            for i in 0..8 {
                self.state[i] = self.state[i].wrapping_add(words[i]);
                let rot_idx = (i + 4) % 8;
                self.state[i] ^= rotr64(self.state[rot_idx], 17);
            }

            // Apply ClockPermute
            clock_permute(&mut self.state);
        }
    }

    /// Update the hasher with new data.
    ///
    /// This method can be called multiple times to feed data incrementally.
    /// The hasher will buffer partial blocks and process complete 128-byte
    /// blocks as they become available.
    ///
    /// # Arguments
    ///
    /// * `data` - The data to hash (can be any length, including empty slices)
    ///
    /// # Examples
    ///
    /// Feeding data in chunks:
    /// ```rust
    /// # use clock_hash::ClockHasher;
    /// let mut hasher = ClockHasher::new();
    ///
    /// // Feed data in multiple calls
    /// hasher.update(b"chunk 1");
    /// hasher.update(b"chunk 2");
    /// hasher.update(b"chunk 3");
    ///
    /// let hash = hasher.finalize();
    /// ```
    ///
    /// Empty updates are allowed:
    /// ```rust
    /// # use clock_hash::ClockHasher;
    /// let mut hasher = ClockHasher::new();
    /// hasher.update(b""); // Empty update - no effect
    /// hasher.update(b"data");
    /// let hash = hasher.finalize();
    /// ```
    pub fn update(&mut self, data: &[u8]) {
        self.message_len = self.message_len.wrapping_add(data.len() as u64);

        let mut data_offset = 0;

        // If we have buffered data, try to complete a block
        if self.buffer_len > 0 {
            let needed = BLOCK_SIZE - self.buffer_len;
            let to_copy = core::cmp::min(needed, data.len());

            self.buffer[self.buffer_len..self.buffer_len + to_copy]
                .copy_from_slice(&data[0..to_copy]);
            self.buffer_len += to_copy;
            data_offset = to_copy;

            // If we completed a block, process it
            if self.buffer_len == BLOCK_SIZE {
                let block: [u8; BLOCK_SIZE] = self.buffer;
                self.process_block(&block);
                self.buffer_len = 0;
            }
        }

        // Process complete blocks from remaining data
        while data_offset + BLOCK_SIZE <= data.len() {
            let mut block = [0u8; BLOCK_SIZE];
            block.copy_from_slice(&data[data_offset..data_offset + BLOCK_SIZE]);
            self.process_block(&block);
            data_offset += BLOCK_SIZE;
        }

        // Buffer remaining partial block
        if data_offset < data.len() {
            let remaining = data.len() - data_offset;
            self.buffer[0..remaining].copy_from_slice(&data[data_offset..]);
            self.buffer_len = remaining;
        }
    }

    /// Finalize the hash and return the result.
    ///
    /// This method applies the final padding scheme, processes any remaining
    /// buffered data, and returns the 32-byte ClockHash-256 hash. After calling
    /// `finalize()`, the hasher is consumed and cannot be used again.
    ///
    /// The padding scheme used is: `message || 0x80 || zeros || length_bits`
    /// where `length_bits` is the total message length in bits as a 64-bit
    /// little-endian value.
    ///
    /// # Returns
    ///
    /// A 32-byte array containing the final ClockHash-256 hash
    ///
    /// # Examples
    ///
    /// Basic finalization:
    /// ```rust
    /// # use clock_hash::ClockHasher;
    /// let mut hasher = ClockHasher::new();
    /// hasher.update(b"test data");
    /// let hash = hasher.finalize();
    /// assert_eq!(hash.len(), 32);
    /// ```
    ///
    /// Empty hash:
    /// ```rust
    /// # use clock_hash::ClockHasher;
    /// let hasher = ClockHasher::new();
    /// let empty_hash = hasher.finalize(); // No data added
    /// // Still produces a valid hash
    /// assert_eq!(empty_hash.len(), 32);
    /// ```
    ///
    /// # Note
    ///
    /// After calling `finalize()`, the hasher cannot be reused.
    /// Create a new `ClockHasher` instance for additional hashes.
    pub fn finalize(mut self) -> [u8; 32] {
        use crate::padding::{MAX_PADDED_SIZE, BLOCK_SIZE};

        // Create padded final block (always maximum size for constant-time)
        let mut final_block = [0u8; MAX_PADDED_SIZE];

        // Copy buffered data
        final_block[0..self.buffer_len].copy_from_slice(&self.buffer[0..self.buffer_len]);

        // Apply padding to maximum size
        pad_message_in_place(
            &mut final_block,
            self.buffer_len,
            0,
            self.message_len as usize,
        );

        // Process all blocks (always exactly MAX_PADDED_SIZE / BLOCK_SIZE blocks)
        let mut offset = 0;
        while offset + BLOCK_SIZE <= MAX_PADDED_SIZE {
            let mut block = [0u8; BLOCK_SIZE];
            block.copy_from_slice(&final_block[offset..offset + BLOCK_SIZE]);
            self.process_block(&block);
            offset += BLOCK_SIZE;
        }

        // Finalization: XOR state with IV
        for i in 0..8 {
            self.state[i] ^= IV[i];
        }

        // Output folding: H[i] = S[i] ⊕ S[i+4] for i=0..3
        let h0 = self.state[0] ^ self.state[4];
        let h1 = self.state[1] ^ self.state[5];
        let h2 = self.state[2] ^ self.state[6];
        let h3 = self.state[3] ^ self.state[7];

        // Convert to bytes (little-endian)
        let mut result = [0u8; 32];
        result[0..8].copy_from_slice(&h0.to_le_bytes());
        result[8..16].copy_from_slice(&h1.to_le_bytes());
        result[16..24].copy_from_slice(&h2.to_le_bytes());
        result[24..32].copy_from_slice(&h3.to_le_bytes());

        result
    }
}

impl Default for ClockHasher {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_hasher_empty() {
        let hasher = ClockHasher::new();
        let hash = hasher.finalize();
        assert_eq!(hash.len(), 32);
    }

    #[test]
    fn test_hasher_small() {
        let mut hasher = ClockHasher::new();
        hasher.update(b"abc");
        let hash = hasher.finalize();
        assert_eq!(hash.len(), 32);
    }

    #[test]
    fn test_hasher_incremental() {
        let mut hasher1 = ClockHasher::new();
        hasher1.update(b"hello");
        hasher1.update(b", ");
        hasher1.update(b"world");
        let hash1 = hasher1.finalize();

        let mut hasher2 = ClockHasher::new();
        hasher2.update(b"hello, world");
        let hash2 = hasher2.finalize();

        // Incremental and one-shot should produce same result
        assert_eq!(hash1, hash2);
    }

    #[test]
    fn test_hasher_large() {
        let mut hasher = ClockHasher::new();
        let data = [0u8; 10000];
        hasher.update(&data);
        let hash = hasher.finalize();
        assert_eq!(hash.len(), 32);
    }
}