zescrow_core/condition/
hashlock.rs

1use bincode::{Decode, Encode};
2#[cfg(feature = "json")]
3use hex::serde as hex_serde;
4#[cfg(feature = "json")]
5use serde::{Deserialize, Serialize};
6use sha2::{Digest, Sha256};
7use subtle::ConstantTimeEq;
8
9#[cfg(feature = "json")]
10use crate::serde::utf8_serde;
11
12/// A hashlock condition requiring SHA-256 preimage verification.
13///
14/// The condition is satisfied when `SHA-256(preimage) == hash`.
15///
16/// # Example
17///
18/// ```
19/// use sha2::{Digest, Sha256};
20/// use zescrow_core::Condition;
21///
22/// let preimage = b"my-secret-preimage".to_vec();
23/// let hash: [u8; 32] = Sha256::digest(&preimage).into();
24///
25/// let condition = Condition::hashlock(hash, preimage);
26/// assert!(condition.verify().is_ok());
27/// ```
28#[cfg_attr(feature = "json", derive(Serialize, Deserialize))]
29#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
30pub struct Hashlock {
31    /// The expected SHA-256 digest of the preimage.
32    #[cfg_attr(feature = "json", serde(with = "hex_serde"))]
33    pub hash: [u8; 32],
34
35    /// Secret preimage as UTF-8 string.
36    #[cfg_attr(feature = "json", serde(with = "utf8_serde"))]
37    pub preimage: Vec<u8>,
38}
39
40impl Hashlock {
41    /// Verifies that `SHA-256(preimage) == hash` using constant-time comparison.
42    ///
43    /// # Errors
44    ///
45    /// Returns [`Error::Mismatch`] if the computed hash does not match.
46    pub fn verify(&self) -> Result<(), Error> {
47        let computed = Sha256::digest(&self.preimage);
48        computed[..]
49            .ct_eq(&self.hash)
50            .unwrap_u8()
51            .eq(&1)
52            .then_some(())
53            .ok_or(Error::Mismatch)
54    }
55}
56
57/// Errors from hashlock verification.
58#[derive(Debug, thiserror::Error)]
59pub enum Error {
60    /// The provided preimage did not hash to the expected value.
61    #[error("SHA256(preimage) != hash")]
62    Mismatch,
63}