bitcoin_hashes/
sha256t.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! SHA256t implementation (tagged SHA256).
4//!
5
6use core::marker::PhantomData;
7use core::ops::Index;
8use core::slice::SliceIndex;
9use core::{cmp, str};
10
11use crate::{sha256, FromSliceError};
12
13type HashEngine = sha256::HashEngine;
14
15/// Trait representing a tag that can be used as a context for SHA256t hashes.
16pub trait Tag {
17    /// Returns a hash engine that is pre-tagged and is ready to be used for the data.
18    fn engine() -> sha256::HashEngine;
19}
20
21/// Output of the SHA256t hash function.
22#[repr(transparent)]
23pub struct Hash<T: Tag>([u8; 32], PhantomData<T>);
24
25#[cfg(feature = "schemars")]
26impl<T: Tag> schemars::JsonSchema for Hash<T> {
27    fn schema_name() -> String { "Hash".to_owned() }
28
29    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
30        let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
31        schema.string = Some(Box::new(schemars::schema::StringValidation {
32            max_length: Some(32 * 2),
33            min_length: Some(32 * 2),
34            pattern: Some("[0-9a-fA-F]+".to_owned()),
35        }));
36        schema.into()
37    }
38}
39
40impl<T: Tag> Hash<T> {
41    fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) }
42
43    fn internal_engine() -> HashEngine { T::engine() }
44}
45
46impl<T: Tag> Copy for Hash<T> {}
47impl<T: Tag> Clone for Hash<T> {
48    fn clone(&self) -> Self { *self }
49}
50impl<T: Tag> PartialEq for Hash<T> {
51    fn eq(&self, other: &Hash<T>) -> bool { self.0 == other.0 }
52}
53impl<T: Tag> Eq for Hash<T> {}
54impl<T: Tag> Default for Hash<T> {
55    fn default() -> Self { Hash([0; 32], PhantomData) }
56}
57impl<T: Tag> PartialOrd for Hash<T> {
58    fn partial_cmp(&self, other: &Hash<T>) -> Option<cmp::Ordering> {
59        Some(cmp::Ord::cmp(self, other))
60    }
61}
62impl<T: Tag> Ord for Hash<T> {
63    fn cmp(&self, other: &Hash<T>) -> cmp::Ordering { cmp::Ord::cmp(&self.0, &other.0) }
64}
65impl<T: Tag> core::hash::Hash for Hash<T> {
66    fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.0.hash(h) }
67}
68
69crate::internal_macros::hash_trait_impls!(256, true, T: Tag);
70
71fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
72    use crate::Hash as _;
73
74    Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array())
75}
76
77/// Macro used to define a newtype tagged hash.
78///
79/// This macro creates two types:
80///
81/// * a tag struct
82/// * a hash wrapper
83///
84/// The syntax is:
85///
86/// ```
87/// # use bitcoin_hashes::sha256t_hash_newtype;
88/// sha256t_hash_newtype! {
89///     /// Optional documentation details here.
90///     /// Summary is always generated.
91///     pub struct FooTag = hash_str("foo");
92///
93///     /// A foo hash.
94///     // Direction works just like in case of hash_newtype! macro.
95///     #[hash_newtype(forward)]
96///     pub struct FooHash(_);
97/// }
98/// ```
99///
100/// The structs must be defined in this order - tag first, then hash type. `hash_str` marker
101/// says the midstate should be generated by hashing the supplied string in a way described in
102/// BIP-341. Alternatively, you can supply `hash_bytes` to hash raw bytes. If you have the midstate
103/// already pre-computed and prefer **compiler** performance to readability you may use
104/// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGTH)` instead.
105///
106/// Both visibility modifiers and attributes are optional and passed to inner structs (excluding
107/// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in
108/// [`hash_newtype`] macro.
109///
110/// The macro accepts multiple inputs so you can define multiple hash newtypes in one macro call.
111/// Just make sure to enter the structs in order `Tag0`, `Hash0`, `Tag1`, `Hash1`...
112///
113/// [`hash_newtype`]: crate::hash_newtype
114#[macro_export]
115macro_rules! sha256t_hash_newtype {
116    ($($(#[$($tag_attr:tt)*])* $tag_vis:vis struct $tag:ident = $constructor:tt($($tag_value:tt)+); $(#[$($hash_attr:tt)*])* $hash_vis:vis struct $hash_name:ident($(#[$($field_attr:tt)*])* _);)+) => {
117        $(
118        $crate::sha256t_hash_newtype_tag!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*);
119
120        impl $crate::sha256t::Tag for $tag {
121            #[inline]
122            fn engine() -> $crate::sha256::HashEngine {
123                const MIDSTATE: ($crate::sha256::Midstate, usize) = $crate::sha256t_hash_newtype_tag_constructor!($constructor, $($tag_value)+);
124                #[allow(unused)]
125                const _LENGTH_CHECK: () = [(); 1][MIDSTATE.1 % 64];
126                $crate::sha256::HashEngine::from_midstate(MIDSTATE.0, MIDSTATE.1)
127            }
128        }
129
130        $crate::hash_newtype! {
131            $(#[$($hash_attr)*])*
132            $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>);
133        }
134        )+
135    }
136}
137
138// Workaround macros being unavailable in attributes.
139#[doc(hidden)]
140#[macro_export]
141macro_rules! sha256t_hash_newtype_tag {
142    ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => {
143        #[doc = "The tag used for [`"]
144        #[doc = $name]
145        #[doc = "`]\n\n"]
146        $(#[$($attr)*])*
147        #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
148        $vis struct $tag;
149    };
150}
151
152#[doc(hidden)]
153#[macro_export]
154macro_rules! sha256t_hash_newtype_tag_constructor {
155    (hash_str, $value:expr) => {
156        ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64)
157    };
158    (hash_bytes, $value:expr) => {
159        ($crate::sha256::Midstate::hash_tag($value), 64)
160    };
161    (raw, $bytes:expr, $len:expr) => {
162        ($crate::sha256::Midstate::from_byte_array($bytes), $len)
163    };
164}
165
166#[cfg(test)]
167mod tests {
168    #[cfg(feature = "alloc")]
169    use crate::Hash;
170    use crate::{sha256, sha256t};
171
172    const TEST_MIDSTATE: [u8; 32] = [
173        156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147,
174        108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201,
175    ];
176
177    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
178    pub struct TestHashTag;
179
180    impl sha256t::Tag for TestHashTag {
181        fn engine() -> sha256::HashEngine {
182            // The TapRoot TapLeaf midstate.
183            let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
184            sha256::HashEngine::from_midstate(midstate, 64)
185        }
186    }
187
188    #[cfg(feature = "schemars")]
189    impl schemars::JsonSchema for TestHashTag {
190        fn schema_name() -> String { "Hash".to_owned() }
191
192        fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
193            let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
194            schema.string = Some(Box::new(schemars::schema::StringValidation {
195                max_length: Some(64 * 2),
196                min_length: Some(64 * 2),
197                pattern: Some("[0-9a-fA-F]+".to_owned()),
198            }));
199            schema.into()
200        }
201    }
202
203    /// A hash tagged with `$name`.
204    #[cfg(feature = "alloc")]
205    pub type TestHash = sha256t::Hash<TestHashTag>;
206
207    sha256t_hash_newtype! {
208        /// Test detailed explanation.
209        struct NewTypeTag = raw(TEST_MIDSTATE, 64);
210
211        /// A test hash.
212        #[hash_newtype(backward)]
213        struct NewTypeHash(_);
214    }
215
216    #[test]
217    #[cfg(feature = "alloc")]
218    fn test_sha256t() {
219        assert_eq!(
220            TestHash::hash(&[0]).to_string(),
221            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
222        );
223        assert_eq!(
224            NewTypeHash::hash(&[0]).to_string(),
225            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
226        );
227    }
228}