clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Domain separation for ClockHash-256
//!
//! Domain separation ensures that hashes computed for different use cases
//! (block headers, transactions, Merkle trees, etc.) produce different outputs
//! even when the input data is identical. This prevents cross-domain collision
//! attacks and maintains the security properties of the hash function.
//!
//! ## Security Importance
//!
//! Without domain separation, an attacker who finds a collision in one domain
//! could potentially use it to attack a different domain. Domain separation
//! ensures that collisions in one domain have no impact on others.
//!
//! ## Implementation
//!
//! Domain separation is implemented by prepending the domain identifier
//! followed by a null byte separator before the actual data:
//!
//! `hash(domain || 0x00 || data)`
//!
//! This approach is simple, efficient, and provides strong separation guarantees.

use crate::hasher::ClockHasher;

/// Predefined domain tag constants for ClockinChain use cases
///
/// These constants define the standard domain separators used throughout
/// the ClockinChain ecosystem. Each domain serves a specific purpose and
/// ensures isolation between different hash applications.
pub mod tags {
    /// Domain tag for block headers
    ///
    /// Used for hashing complete block headers including previous hash,
    /// merkle root, timestamp, difficulty, and nonce.
    ///
    /// # Example
    /// ```rust
    /// # use clock_hash::{clockhash256_domain, tags};
    /// let block_header = b"prev_hash|merkle_root|timestamp|difficulty|nonce";
    /// let block_hash = clockhash256_domain(tags::CLK_BLOCK, block_header);
    /// ```
    pub const CLK_BLOCK: &[u8] = b"CLK-BLOCK";

    /// Domain tag for transaction identifiers
    ///
    /// Used for computing transaction IDs from transaction data including
    /// inputs, outputs, lock time, and other metadata.
    ///
    /// # Example
    /// ```rust
    /// # use clock_hash::{clockhash256_domain, tags};
    /// let tx_data = b"inputs|outputs|lock_time|version";
    /// let tx_id = clockhash256_domain(tags::CLK_TX, tx_data);
    /// ```
    pub const CLK_TX: &[u8] = b"CLK-TX";

    /// Domain tag for Merkle tree nodes
    ///
    /// Used for constructing Merkle trees where internal nodes are hashes
    /// of concatenated child hashes.
    ///
    /// # Example
    /// ```rust
    /// # use clock_hash::{clockhash256_domain, tags};
    /// let left_data = b"left child data";
    /// let right_data = b"right child data";
    /// let left_hash = clockhash256_domain(tags::CLK_MERKLE, left_data);
    /// let right_hash = clockhash256_domain(tags::CLK_MERKLE, right_data);
    /// let parent_data = [left_hash, right_hash].concat();
    /// let parent_hash = clockhash256_domain(tags::CLK_MERKLE, &parent_data);
    /// ```
    pub const CLK_MERKLE: &[u8] = b"CLK-MERKLE";

    /// Domain tag for signature nonce derivation
    ///
    /// Used for deterministic nonce generation in digital signatures to
    /// prevent nonce reuse attacks while maintaining deterministic behavior.
    ///
    /// # Example
    /// ```rust
    /// # use clock_hash::{clockhash256_domain, tags};
    /// let nonce_seed = b"private_key|message_hash|counter";
    /// let nonce = clockhash256_domain(tags::CLK_NONCE, nonce_seed);
    /// ```
    pub const CLK_NONCE: &[u8] = b"CLK-NONCE";

    /// Domain tag for deterministic random number generation
    ///
    /// Used for seeding deterministic RNGs that need cryptographic strength
    /// and domain isolation from other random number uses.
    ///
    /// # Example
    /// ```rust
    /// # use clock_hash::{clockhash256_domain, tags};
    /// let rng_seed = b"user_entropy|domain_info|counter";
    /// let rng_key = clockhash256_domain(tags::CLK_RNG, rng_seed);
    /// ```
    pub const CLK_RNG: &[u8] = b"CLK-RNG";
}

/// Type-safe domain tag enumeration for ClockinChain use cases
///
/// `DomainTag` provides compile-time type safety for domain separation.
/// Each variant corresponds to a specific use case and maps to the
/// appropriate domain tag bytes. This prevents typos and ensures
/// consistent domain usage across the codebase.
///
/// # Examples
///
/// Using with typed domains:
/// ```rust
/// use clock_hash::{clockhash256_with_domain, DomainTag};
///
/// let block_data = b"block header data";
/// let tx_data = b"transaction data";
///
/// let block_hash = clockhash256_with_domain(DomainTag::Block, block_data);
/// let tx_hash = clockhash256_with_domain(DomainTag::Transaction, tx_data);
///
/// assert_ne!(block_hash, tx_hash); // Guaranteed to be different
/// ```
///
/// Converting to bytes:
/// ```rust
/// # use clock_hash::DomainTag;
/// let tag = DomainTag::Merkle;
/// let bytes = tag.as_bytes();
/// assert_eq!(bytes, b"CLK-MERKLE");
/// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DomainTag {
    /// Block header hashing domain
    ///
    /// For hashing complete block headers in the blockchain
    Block,
    /// Transaction identifier domain
    ///
    /// For computing unique transaction IDs
    Transaction,
    /// Merkle tree construction domain
    ///
    /// For building Merkle trees and computing merkle roots
    Merkle,
    /// Signature nonce derivation domain
    ///
    /// For deterministic nonce generation in signatures
    Nonce,
    /// Deterministic RNG seeding domain
    ///
    /// For seeding cryptographic random number generators
    Rng,
}

impl DomainTag {
    /// Get the byte representation of the domain tag
    #[inline]
    pub fn as_bytes(self) -> &'static [u8] {
        match self {
            DomainTag::Block => tags::CLK_BLOCK,
            DomainTag::Transaction => tags::CLK_TX,
            DomainTag::Merkle => tags::CLK_MERKLE,
            DomainTag::Nonce => tags::CLK_NONCE,
            DomainTag::Rng => tags::CLK_RNG,
        }
    }
}

/// Compute ClockHash-256 with custom domain separation.
///
/// This function provides domain separation by prepending the domain identifier
/// followed by a null byte separator before hashing the actual data. This ensures
/// that identical data produces different hashes when used in different domains,
/// preventing cross-domain collision attacks.
///
/// # Arguments
///
/// * `domain` - Custom domain identifier bytes (must be consistent for the same domain)
/// * `data` - The data to hash
///
/// # Returns
///
/// A 32-byte array containing the domain-separated ClockHash-256 hash
///
/// # Examples
///
/// Using custom domain tags:
/// ```rust
/// # use clock_hash::clockhash256_domain;
/// let data = b"some data";
///
/// let domain1 = b"MY-APP-V1";
/// let domain2 = b"MY-APP-V2";
///
/// let hash1 = clockhash256_domain(domain1, data);
/// let hash2 = clockhash256_domain(domain2, data);
///
/// assert_ne!(hash1, hash2); // Different domains = different hashes
/// ```
///
/// Custom domain vs no domain:
/// ```rust
/// # use clock_hash::{clockhash256_domain, clockhash256};
/// let data = b"test";
/// let custom_hash = clockhash256_domain(b"CUSTOM", data);
/// let plain_hash = clockhash256(data);
///
/// assert_ne!(custom_hash, plain_hash); // Domain separation works
/// ```
///
/// # Security Notes
///
/// - Domain identifiers should be unique and consistent within your application
/// - Never use the same domain for different purposes
/// - Domain separation is critical for maintaining hash function security properties
#[inline]
pub fn clockhash256_domain(domain: &[u8], data: &[u8]) -> [u8; 32] {
    let mut hasher = ClockHasher::new();

    // Prepend domain tag and separator to achieve domain separation
    // Format: domain || 0x00 || data
    hasher.update(domain);
    hasher.update(&[0x00]);
    hasher.update(data);

    hasher.finalize()
}

/// Compute ClockHash-256 with a typed domain tag.
///
/// This function provides type-safe domain separation using the `DomainTag` enum.
/// It automatically converts the enum variant to the appropriate domain bytes
/// and performs domain-separated hashing.
///
/// # Arguments
///
/// * `domain` - The domain tag enum variant specifying the use case
/// * `data` - The data to hash
///
/// # Returns
///
/// A 32-byte array containing the domain-separated ClockHash-256 hash
///
/// # Examples
///
/// Type-safe domain hashing:
/// ```rust
/// use clock_hash::{clockhash256_with_domain, DomainTag};
///
/// let data = b"important data";
///
/// // Type-safe domain specification
/// let block_hash = clockhash256_with_domain(DomainTag::Block, data);
/// let merkle_hash = clockhash256_with_domain(DomainTag::Merkle, data);
///
/// assert_ne!(block_hash, merkle_hash);
/// ```
///
/// All domain types:
/// ```rust
/// # use clock_hash::{clockhash256_with_domain, DomainTag};
/// # let data = b"test";
/// let block_hash = clockhash256_with_domain(DomainTag::Block, data);
/// let tx_hash = clockhash256_with_domain(DomainTag::Transaction, data);
/// let merkle_hash = clockhash256_with_domain(DomainTag::Merkle, data);
/// let nonce_hash = clockhash256_with_domain(DomainTag::Nonce, data);
/// let rng_hash = clockhash256_with_domain(DomainTag::Rng, data);
///
/// // All hashes are guaranteed to be different
/// let hashes = vec![block_hash, tx_hash, merkle_hash, nonce_hash, rng_hash];
/// for i in 0..hashes.len() {
///     for j in (i+1)..hashes.len() {
///         assert_ne!(hashes[i], hashes[j]);
///     }
/// }
/// ```
///
/// # Performance
///
/// This function has the same performance characteristics as `clockhash256_domain()`
/// since it simply delegates to that function after converting the enum to bytes.
#[inline]
pub fn clockhash256_with_domain(domain: DomainTag, data: &[u8]) -> [u8; 32] {
    clockhash256_domain(domain.as_bytes(), data)
}

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

    #[test]
    fn test_domain_separation() {
        let data = b"test data";

        // Different domains should produce different hashes
        let hash1 = clockhash256_domain(tags::CLK_BLOCK, data);
        let hash2 = clockhash256_domain(tags::CLK_TX, data);
        let hash3 = clockhash256(data);

        assert_ne!(
            hash1, hash2,
            "Different domains should produce different hashes"
        );
        assert_ne!(
            hash1, hash3,
            "Domain-separated hash should differ from raw hash"
        );
        assert_ne!(
            hash2, hash3,
            "Domain-separated hash should differ from raw hash"
        );
    }

    #[test]
    fn test_domain_tag_enum() {
        let data = b"test";

        let hash_block = clockhash256_with_domain(DomainTag::Block, data);
        let hash_tx = clockhash256_with_domain(DomainTag::Transaction, data);
        let hash_merkle = clockhash256_with_domain(DomainTag::Merkle, data);

        assert_ne!(hash_block, hash_tx);
        assert_ne!(hash_block, hash_merkle);
        assert_ne!(hash_tx, hash_merkle);
    }

    #[test]
    fn test_domain_deterministic() {
        let data = b"deterministic test";

        let hash1 = clockhash256_domain(tags::CLK_BLOCK, data);
        let hash2 = clockhash256_domain(tags::CLK_BLOCK, data);

        assert_eq!(
            hash1, hash2,
            "Same domain and data should produce same hash"
        );
    }
}