clock_hash/domain.rs
1//! Domain separation for ClockHash-256
2//!
3//! Domain separation ensures that hashes computed for different use cases
4//! (block headers, transactions, Merkle trees, etc.) produce different outputs
5//! even when the input data is identical. This prevents cross-domain collision
6//! attacks and maintains the security properties of the hash function.
7//!
8//! ## Security Importance
9//!
10//! Without domain separation, an attacker who finds a collision in one domain
11//! could potentially use it to attack a different domain. Domain separation
12//! ensures that collisions in one domain have no impact on others.
13//!
14//! ## Implementation
15//!
16//! Domain separation is implemented by prepending the domain identifier
17//! followed by a null byte separator before the actual data:
18//!
19//! `hash(domain || 0x00 || data)`
20//!
21//! This approach is simple, efficient, and provides strong separation guarantees.
22
23use crate::hasher::ClockHasher;
24
25/// Predefined domain tag constants for ClockinChain use cases
26///
27/// These constants define the standard domain separators used throughout
28/// the ClockinChain ecosystem. Each domain serves a specific purpose and
29/// ensures isolation between different hash applications.
30pub mod tags {
31 /// Domain tag for block headers
32 ///
33 /// Used for hashing complete block headers including previous hash,
34 /// merkle root, timestamp, difficulty, and nonce.
35 ///
36 /// # Example
37 /// ```rust
38 /// # use clock_hash::{clockhash256_domain, tags};
39 /// let block_header = b"prev_hash|merkle_root|timestamp|difficulty|nonce";
40 /// let block_hash = clockhash256_domain(tags::CLK_BLOCK, block_header);
41 /// ```
42 pub const CLK_BLOCK: &[u8] = b"CLK-BLOCK";
43
44 /// Domain tag for transaction identifiers
45 ///
46 /// Used for computing transaction IDs from transaction data including
47 /// inputs, outputs, lock time, and other metadata.
48 ///
49 /// # Example
50 /// ```rust
51 /// # use clock_hash::{clockhash256_domain, tags};
52 /// let tx_data = b"inputs|outputs|lock_time|version";
53 /// let tx_id = clockhash256_domain(tags::CLK_TX, tx_data);
54 /// ```
55 pub const CLK_TX: &[u8] = b"CLK-TX";
56
57 /// Domain tag for Merkle tree nodes
58 ///
59 /// Used for constructing Merkle trees where internal nodes are hashes
60 /// of concatenated child hashes.
61 ///
62 /// # Example
63 /// ```rust
64 /// # use clock_hash::{clockhash256_domain, tags};
65 /// let left_data = b"left child data";
66 /// let right_data = b"right child data";
67 /// let left_hash = clockhash256_domain(tags::CLK_MERKLE, left_data);
68 /// let right_hash = clockhash256_domain(tags::CLK_MERKLE, right_data);
69 /// let parent_data = [left_hash, right_hash].concat();
70 /// let parent_hash = clockhash256_domain(tags::CLK_MERKLE, &parent_data);
71 /// ```
72 pub const CLK_MERKLE: &[u8] = b"CLK-MERKLE";
73
74 /// Domain tag for signature nonce derivation
75 ///
76 /// Used for deterministic nonce generation in digital signatures to
77 /// prevent nonce reuse attacks while maintaining deterministic behavior.
78 ///
79 /// # Example
80 /// ```rust
81 /// # use clock_hash::{clockhash256_domain, tags};
82 /// let nonce_seed = b"private_key|message_hash|counter";
83 /// let nonce = clockhash256_domain(tags::CLK_NONCE, nonce_seed);
84 /// ```
85 pub const CLK_NONCE: &[u8] = b"CLK-NONCE";
86
87 /// Domain tag for deterministic random number generation
88 ///
89 /// Used for seeding deterministic RNGs that need cryptographic strength
90 /// and domain isolation from other random number uses.
91 ///
92 /// # Example
93 /// ```rust
94 /// # use clock_hash::{clockhash256_domain, tags};
95 /// let rng_seed = b"user_entropy|domain_info|counter";
96 /// let rng_key = clockhash256_domain(tags::CLK_RNG, rng_seed);
97 /// ```
98 pub const CLK_RNG: &[u8] = b"CLK-RNG";
99}
100
101/// Type-safe domain tag enumeration for ClockinChain use cases
102///
103/// `DomainTag` provides compile-time type safety for domain separation.
104/// Each variant corresponds to a specific use case and maps to the
105/// appropriate domain tag bytes. This prevents typos and ensures
106/// consistent domain usage across the codebase.
107///
108/// # Examples
109///
110/// Using with typed domains:
111/// ```rust
112/// use clock_hash::{clockhash256_with_domain, DomainTag};
113///
114/// let block_data = b"block header data";
115/// let tx_data = b"transaction data";
116///
117/// let block_hash = clockhash256_with_domain(DomainTag::Block, block_data);
118/// let tx_hash = clockhash256_with_domain(DomainTag::Transaction, tx_data);
119///
120/// assert_ne!(block_hash, tx_hash); // Guaranteed to be different
121/// ```
122///
123/// Converting to bytes:
124/// ```rust
125/// # use clock_hash::DomainTag;
126/// let tag = DomainTag::Merkle;
127/// let bytes = tag.as_bytes();
128/// assert_eq!(bytes, b"CLK-MERKLE");
129/// ```
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub enum DomainTag {
132 /// Block header hashing domain
133 ///
134 /// For hashing complete block headers in the blockchain
135 Block,
136 /// Transaction identifier domain
137 ///
138 /// For computing unique transaction IDs
139 Transaction,
140 /// Merkle tree construction domain
141 ///
142 /// For building Merkle trees and computing merkle roots
143 Merkle,
144 /// Signature nonce derivation domain
145 ///
146 /// For deterministic nonce generation in signatures
147 Nonce,
148 /// Deterministic RNG seeding domain
149 ///
150 /// For seeding cryptographic random number generators
151 Rng,
152}
153
154impl DomainTag {
155 /// Get the byte representation of the domain tag
156 #[inline]
157 pub fn as_bytes(self) -> &'static [u8] {
158 match self {
159 DomainTag::Block => tags::CLK_BLOCK,
160 DomainTag::Transaction => tags::CLK_TX,
161 DomainTag::Merkle => tags::CLK_MERKLE,
162 DomainTag::Nonce => tags::CLK_NONCE,
163 DomainTag::Rng => tags::CLK_RNG,
164 }
165 }
166}
167
168/// Compute ClockHash-256 with custom domain separation.
169///
170/// This function provides domain separation by prepending the domain identifier
171/// followed by a null byte separator before hashing the actual data. This ensures
172/// that identical data produces different hashes when used in different domains,
173/// preventing cross-domain collision attacks.
174///
175/// # Arguments
176///
177/// * `domain` - Custom domain identifier bytes (must be consistent for the same domain)
178/// * `data` - The data to hash
179///
180/// # Returns
181///
182/// A 32-byte array containing the domain-separated ClockHash-256 hash
183///
184/// # Examples
185///
186/// Using custom domain tags:
187/// ```rust
188/// # use clock_hash::clockhash256_domain;
189/// let data = b"some data";
190///
191/// let domain1 = b"MY-APP-V1";
192/// let domain2 = b"MY-APP-V2";
193///
194/// let hash1 = clockhash256_domain(domain1, data);
195/// let hash2 = clockhash256_domain(domain2, data);
196///
197/// assert_ne!(hash1, hash2); // Different domains = different hashes
198/// ```
199///
200/// Custom domain vs no domain:
201/// ```rust
202/// # use clock_hash::{clockhash256_domain, clockhash256};
203/// let data = b"test";
204/// let custom_hash = clockhash256_domain(b"CUSTOM", data);
205/// let plain_hash = clockhash256(data);
206///
207/// assert_ne!(custom_hash, plain_hash); // Domain separation works
208/// ```
209///
210/// # Security Notes
211///
212/// - Domain identifiers should be unique and consistent within your application
213/// - Never use the same domain for different purposes
214/// - Domain separation is critical for maintaining hash function security properties
215#[inline]
216pub fn clockhash256_domain(domain: &[u8], data: &[u8]) -> [u8; 32] {
217 let mut hasher = ClockHasher::new();
218
219 // Prepend domain tag and separator to achieve domain separation
220 // Format: domain || 0x00 || data
221 hasher.update(domain);
222 hasher.update(&[0x00]);
223 hasher.update(data);
224
225 hasher.finalize()
226}
227
228/// Compute ClockHash-256 with a typed domain tag.
229///
230/// This function provides type-safe domain separation using the `DomainTag` enum.
231/// It automatically converts the enum variant to the appropriate domain bytes
232/// and performs domain-separated hashing.
233///
234/// # Arguments
235///
236/// * `domain` - The domain tag enum variant specifying the use case
237/// * `data` - The data to hash
238///
239/// # Returns
240///
241/// A 32-byte array containing the domain-separated ClockHash-256 hash
242///
243/// # Examples
244///
245/// Type-safe domain hashing:
246/// ```rust
247/// use clock_hash::{clockhash256_with_domain, DomainTag};
248///
249/// let data = b"important data";
250///
251/// // Type-safe domain specification
252/// let block_hash = clockhash256_with_domain(DomainTag::Block, data);
253/// let merkle_hash = clockhash256_with_domain(DomainTag::Merkle, data);
254///
255/// assert_ne!(block_hash, merkle_hash);
256/// ```
257///
258/// All domain types:
259/// ```rust
260/// # use clock_hash::{clockhash256_with_domain, DomainTag};
261/// # let data = b"test";
262/// let block_hash = clockhash256_with_domain(DomainTag::Block, data);
263/// let tx_hash = clockhash256_with_domain(DomainTag::Transaction, data);
264/// let merkle_hash = clockhash256_with_domain(DomainTag::Merkle, data);
265/// let nonce_hash = clockhash256_with_domain(DomainTag::Nonce, data);
266/// let rng_hash = clockhash256_with_domain(DomainTag::Rng, data);
267///
268/// // All hashes are guaranteed to be different
269/// let hashes = vec![block_hash, tx_hash, merkle_hash, nonce_hash, rng_hash];
270/// for i in 0..hashes.len() {
271/// for j in (i+1)..hashes.len() {
272/// assert_ne!(hashes[i], hashes[j]);
273/// }
274/// }
275/// ```
276///
277/// # Performance
278///
279/// This function has the same performance characteristics as `clockhash256_domain()`
280/// since it simply delegates to that function after converting the enum to bytes.
281#[inline]
282pub fn clockhash256_with_domain(domain: DomainTag, data: &[u8]) -> [u8; 32] {
283 clockhash256_domain(domain.as_bytes(), data)
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use crate::clockhash256;
290
291 #[test]
292 fn test_domain_separation() {
293 let data = b"test data";
294
295 // Different domains should produce different hashes
296 let hash1 = clockhash256_domain(tags::CLK_BLOCK, data);
297 let hash2 = clockhash256_domain(tags::CLK_TX, data);
298 let hash3 = clockhash256(data);
299
300 assert_ne!(
301 hash1, hash2,
302 "Different domains should produce different hashes"
303 );
304 assert_ne!(
305 hash1, hash3,
306 "Domain-separated hash should differ from raw hash"
307 );
308 assert_ne!(
309 hash2, hash3,
310 "Domain-separated hash should differ from raw hash"
311 );
312 }
313
314 #[test]
315 fn test_domain_tag_enum() {
316 let data = b"test";
317
318 let hash_block = clockhash256_with_domain(DomainTag::Block, data);
319 let hash_tx = clockhash256_with_domain(DomainTag::Transaction, data);
320 let hash_merkle = clockhash256_with_domain(DomainTag::Merkle, data);
321
322 assert_ne!(hash_block, hash_tx);
323 assert_ne!(hash_block, hash_merkle);
324 assert_ne!(hash_tx, hash_merkle);
325 }
326
327 #[test]
328 fn test_domain_deterministic() {
329 let data = b"deterministic test";
330
331 let hash1 = clockhash256_domain(tags::CLK_BLOCK, data);
332 let hash2 = clockhash256_domain(tags::CLK_BLOCK, data);
333
334 assert_eq!(
335 hash1, hash2,
336 "Same domain and data should produce same hash"
337 );
338 }
339}