rustywallet_taproot/
tagged_hash.rs

1//! Tagged hashes for BIP340/341
2//!
3//! Tagged hashes provide domain separation: SHA256(SHA256(tag) || SHA256(tag) || data)
4
5use sha2::{Digest, Sha256};
6
7/// Predefined tag for TapLeaf hash
8pub const TAG_TAP_LEAF: &str = "TapLeaf";
9/// Predefined tag for TapBranch hash
10pub const TAG_TAP_BRANCH: &str = "TapBranch";
11/// Predefined tag for TapTweak hash
12pub const TAG_TAP_TWEAK: &str = "TapTweak";
13/// Predefined tag for TapSighash
14pub const TAG_TAP_SIGHASH: &str = "TapSighash";
15/// Predefined tag for BIP340 challenge
16pub const TAG_BIP340_CHALLENGE: &str = "BIP0340/challenge";
17/// Predefined tag for BIP340 aux
18pub const TAG_BIP340_AUX: &str = "BIP0340/aux";
19/// Predefined tag for BIP340 nonce
20pub const TAG_BIP340_NONCE: &str = "BIP0340/nonce";
21
22/// Compute a tagged hash: SHA256(SHA256(tag) || SHA256(tag) || data)
23pub fn tagged_hash(tag: &str, data: &[u8]) -> [u8; 32] {
24    let tag_hash = Sha256::digest(tag.as_bytes());
25    
26    let mut hasher = Sha256::new();
27    hasher.update(tag_hash);
28    hasher.update(tag_hash);
29    hasher.update(data);
30    
31    let result = hasher.finalize();
32    let mut output = [0u8; 32];
33    output.copy_from_slice(&result);
34    output
35}
36
37/// TapLeaf hash
38#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
39pub struct TapLeafHash(pub [u8; 32]);
40
41impl TapLeafHash {
42    /// Compute TapLeaf hash from leaf version and script
43    pub fn from_script(leaf_version: u8, script: &[u8]) -> Self {
44        let mut data = Vec::with_capacity(1 + script.len());
45        data.push(leaf_version);
46        data.extend_from_slice(script);
47        Self(tagged_hash(TAG_TAP_LEAF, &data))
48    }
49
50    /// Get the inner bytes
51    pub fn as_bytes(&self) -> &[u8; 32] {
52        &self.0
53    }
54
55    /// Convert to byte array
56    pub fn to_bytes(self) -> [u8; 32] {
57        self.0
58    }
59}
60
61impl AsRef<[u8]> for TapLeafHash {
62    fn as_ref(&self) -> &[u8] {
63        &self.0
64    }
65}
66
67/// TapBranch/TapNode hash
68#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
69pub struct TapNodeHash(pub [u8; 32]);
70
71impl TapNodeHash {
72    /// Create from raw bytes
73    pub fn from_bytes(bytes: [u8; 32]) -> Self {
74        Self(bytes)
75    }
76
77    /// Compute TapBranch hash from two child hashes
78    /// Children are sorted lexicographically before hashing
79    pub fn from_children(left: &TapNodeHash, right: &TapNodeHash) -> Self {
80        let mut data = [0u8; 64];
81        
82        // Sort lexicographically
83        if left.0 <= right.0 {
84            data[..32].copy_from_slice(&left.0);
85            data[32..].copy_from_slice(&right.0);
86        } else {
87            data[..32].copy_from_slice(&right.0);
88            data[32..].copy_from_slice(&left.0);
89        }
90        
91        Self(tagged_hash(TAG_TAP_BRANCH, &data))
92    }
93
94    /// Create from a leaf hash
95    pub fn from_leaf(leaf: TapLeafHash) -> Self {
96        Self(leaf.0)
97    }
98
99    /// Get the inner bytes
100    pub fn as_bytes(&self) -> &[u8; 32] {
101        &self.0
102    }
103
104    /// Convert to byte array
105    pub fn to_bytes(self) -> [u8; 32] {
106        self.0
107    }
108}
109
110impl AsRef<[u8]> for TapNodeHash {
111    fn as_ref(&self) -> &[u8] {
112        &self.0
113    }
114}
115
116impl From<TapLeafHash> for TapNodeHash {
117    fn from(leaf: TapLeafHash) -> Self {
118        Self(leaf.0)
119    }
120}
121
122/// TapTweak hash
123#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
124pub struct TapTweakHash(pub [u8; 32]);
125
126impl TapTweakHash {
127    /// Compute TapTweak hash from internal key and merkle root
128    pub fn from_key_and_merkle_root(internal_key: &[u8; 32], merkle_root: Option<&TapNodeHash>) -> Self {
129        let mut data = Vec::with_capacity(64);
130        data.extend_from_slice(internal_key);
131        if let Some(root) = merkle_root {
132            data.extend_from_slice(&root.0);
133        }
134        Self(tagged_hash(TAG_TAP_TWEAK, &data))
135    }
136
137    /// Get the inner bytes
138    pub fn as_bytes(&self) -> &[u8; 32] {
139        &self.0
140    }
141
142    /// Convert to byte array
143    pub fn to_bytes(self) -> [u8; 32] {
144        self.0
145    }
146}
147
148impl AsRef<[u8]> for TapTweakHash {
149    fn as_ref(&self) -> &[u8] {
150        &self.0
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn test_tagged_hash() {
160        // Test that tagged hash produces consistent results
161        let hash1 = tagged_hash("test", b"data");
162        let hash2 = tagged_hash("test", b"data");
163        assert_eq!(hash1, hash2);
164
165        // Different tags produce different hashes
166        let hash3 = tagged_hash("other", b"data");
167        assert_ne!(hash1, hash3);
168
169        // Different data produces different hashes
170        let hash4 = tagged_hash("test", b"other");
171        assert_ne!(hash1, hash4);
172    }
173
174    #[test]
175    fn test_tap_leaf_hash() {
176        let script = vec![0x51]; // OP_1
177        let leaf_hash = TapLeafHash::from_script(0xc0, &script);
178        
179        // Should be deterministic
180        let leaf_hash2 = TapLeafHash::from_script(0xc0, &script);
181        assert_eq!(leaf_hash, leaf_hash2);
182    }
183
184    #[test]
185    fn test_tap_branch_hash_ordering() {
186        let left = TapNodeHash([0x01; 32]);
187        let right = TapNodeHash([0x02; 32]);
188
189        // Order shouldn't matter - children are sorted
190        let hash1 = TapNodeHash::from_children(&left, &right);
191        let hash2 = TapNodeHash::from_children(&right, &left);
192        assert_eq!(hash1, hash2);
193    }
194
195    #[test]
196    fn test_tap_tweak_hash() {
197        let internal_key = [0x02; 32];
198        
199        // Without merkle root
200        let tweak1 = TapTweakHash::from_key_and_merkle_root(&internal_key, None);
201        
202        // With merkle root
203        let merkle_root = TapNodeHash([0x03; 32]);
204        let tweak2 = TapTweakHash::from_key_and_merkle_root(&internal_key, Some(&merkle_root));
205        
206        assert_ne!(tweak1.0, tweak2.0);
207    }
208}