// SPDX-License-Identifier: CC0-1.0 //! SHA256t implementation (tagged SHA256). use core::cmp; use core::marker::PhantomData; use core::ops::Index; use core::slice::SliceIndex; use crate::{sha256, FromSliceError}; type HashEngine = sha256::HashEngine; /// Trait representing a tag that can be used as a context for SHA256t hashes. pub trait Tag { /// Returns a hash engine that is pre-tagged and is ready to be used for the data. fn engine() -> sha256::HashEngine; } /// Output of the SHA256t hash function. #[repr(transparent)] pub struct Hash([u8; 32], PhantomData); #[cfg(feature = "schemars")] impl schemars::JsonSchema for Hash { fn schema_name() -> String { "Hash".to_owned() } fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { let mut schema: schemars::schema::SchemaObject = ::json_schema(gen).into(); schema.string = Some(Box::new(schemars::schema::StringValidation { max_length: Some(32 * 2), min_length: Some(32 * 2), pattern: Some("[0-9a-fA-F]+".to_owned()), })); schema.into() } } impl Hash { fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, Default::default()) } fn internal_engine() -> HashEngine { T::engine() } /// Zero cost conversion between a fixed length byte array shared reference and /// a shared reference to this Hash type. pub fn from_bytes_ref(bytes: &[u8; 32]) -> &Self { // Safety: Sound because Self is #[repr(transparent)] containing [u8; 32] unsafe { &*(bytes as *const _ as *const Self) } } /// Zero cost conversion between a fixed length byte array exclusive reference and /// an exclusive reference to this Hash type. pub fn from_bytes_mut(bytes: &mut [u8; 32]) -> &mut Self { // Safety: Sound because Self is #[repr(transparent)] containing [u8; 32] unsafe { &mut *(bytes as *mut _ as *mut Self) } } } impl Copy for Hash {} impl Clone for Hash { fn clone(&self) -> Self { *self } } impl PartialEq for Hash { fn eq(&self, other: &Hash) -> bool { self.0 == other.0 } } impl Eq for Hash {} impl Default for Hash { fn default() -> Self { Hash([0; 32], PhantomData) } } impl PartialOrd for Hash { fn partial_cmp(&self, other: &Hash) -> Option { Some(cmp::Ord::cmp(self, other)) } } impl Ord for Hash { fn cmp(&self, other: &Hash) -> cmp::Ordering { cmp::Ord::cmp(&self.0, &other.0) } } impl core::hash::Hash for Hash { fn hash(&self, h: &mut H) { self.0.hash(h) } } crate::internal_macros::hash_trait_impls!(256, false, T: Tag); fn from_engine(e: sha256::HashEngine) -> Hash { use crate::Hash as _; Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array()) } /// Macro used to define a newtype tagged hash. /// /// This macro creates two types: /// /// * a tag struct /// * a hash wrapper /// /// The syntax is: /// /// ``` /// # use bitcoin_hashes::sha256t_hash_newtype; /// sha256t_hash_newtype! { /// /// Optional documentation details here. /// /// Summary is always generated. /// pub struct FooTag = hash_str("foo"); /// /// /// A foo hash. /// // Direction works just like the hash_newtype! macro. /// #[hash_newtype(backward)] /// pub struct FooHash(_); /// } /// ``` /// /// The structs must be defined in this order - tag first, then hash type. `hash_str` marker /// says the midstate should be generated by hashing the supplied string in a way described in /// BIP-341. Alternatively, you can supply `hash_bytes` to hash raw bytes. If you have the midstate /// already pre-computed and prefer **compiler** performance to readability you may use /// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGTH)` instead. /// /// Both visibility modifiers and attributes are optional and passed to inner structs (excluding /// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in /// [`hash_newtype`] macro. /// /// The macro accepts multiple inputs so you can define multiple hash newtypes in one macro call. /// Just make sure to enter the structs in order `Tag0`, `Hash0`, `Tag1`, `Hash1`... /// /// [`hash_newtype`]: crate::hash_newtype #[macro_export] macro_rules! sha256t_hash_newtype { ($($(#[$($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)*])* _);)+) => { $( $crate::sha256t_hash_newtype_tag!($tag_vis, $tag, stringify!($hash_name), $(#[$($tag_attr)*])*); impl $crate::sha256t::Tag for $tag { #[inline] fn engine() -> $crate::sha256::HashEngine { const MIDSTATE: ($crate::sha256::Midstate, usize) = $crate::sha256t_hash_newtype_tag_constructor!($constructor, $($tag_value)+); #[allow(unused)] const _LENGTH_CHECK: () = [(); 1][MIDSTATE.1 % 64]; $crate::sha256::HashEngine::from_midstate(MIDSTATE.0, MIDSTATE.1) } } $crate::hash_newtype! { $(#[$($hash_attr)*])* $hash_vis struct $hash_name($(#[$($field_attr)*])* $crate::sha256t::Hash<$tag>); } )+ } } // Workaround macros being unavailable in attributes. #[doc(hidden)] #[macro_export] macro_rules! sha256t_hash_newtype_tag { ($vis:vis, $tag:ident, $name:expr, $(#[$($attr:meta)*])*) => { #[doc = "The tag used for [`"] #[doc = $name] #[doc = "`].\n\n"] $(#[$($attr)*])* #[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] $vis struct $tag; }; } #[doc(hidden)] #[macro_export] macro_rules! sha256t_hash_newtype_tag_constructor { (hash_str, $value:expr) => { ($crate::sha256::Midstate::hash_tag($value.as_bytes()), 64) }; (hash_bytes, $value:expr) => { ($crate::sha256::Midstate::hash_tag($value), 64) }; (raw, $bytes:expr, $len:expr) => { ($crate::sha256::Midstate::from_byte_array($bytes), $len) }; } #[cfg(test)] mod tests { #[cfg(feature = "alloc")] use crate::Hash; use crate::{sha256, sha256t}; const TEST_MIDSTATE: [u8; 32] = [ 156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147, 108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201, ]; // The digest created by sha256 hashing `&[0]` starting with `TEST_MIDSTATE`. #[cfg(feature = "alloc")] const HASH_ZERO_BACKWARD: &str = "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"; // And the same thing, forward. #[cfg(feature = "alloc")] const HASH_ZERO_FORWARD: &str = "ed1382037800c9dd938dd8854f1a8863bcdeb6705069b4b56a66ec22519d5829"; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] pub struct TestHashTag; impl sha256t::Tag for TestHashTag { fn engine() -> sha256::HashEngine { // The TapRoot TapLeaf midstate. let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE); sha256::HashEngine::from_midstate(midstate, 64) } } // We support manually implementing `Tag` and creating a tagged hash from it. #[cfg(feature = "alloc")] pub type TestHash = sha256t::Hash; #[test] #[cfg(feature = "alloc")] fn manually_created_sha256t_hash_type() { assert_eq!(TestHash::hash(&[0]).to_string(), HASH_ZERO_FORWARD); } // We also provide a macro to create the tag and the hash type. sha256t_hash_newtype! { /// Test detailed explanation. struct NewTypeTagBackward = raw(TEST_MIDSTATE, 64); /// A test hash. #[hash_newtype(backward)] struct NewTypeHashBackward(_); } #[test] #[cfg(feature = "alloc")] fn macro_created_sha256t_hash_type_backward() { assert_eq!(NewTypeHashBackward::hash(&[0]).to_string(), HASH_ZERO_BACKWARD); } // We also provide a macro to create the tag and the hash type. sha256t_hash_newtype! { /// Test detailed explanation. struct NewTypeTagForward = raw(TEST_MIDSTATE, 64); /// A test hash. #[hash_newtype(forward)] struct NewTypeHashForward(_); } #[test] #[cfg(feature = "alloc")] fn macro_created_sha256t_hash_type_prints_forward() { assert_eq!(NewTypeHashForward::hash(&[0]).to_string(), HASH_ZERO_FORWARD); } }