diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index a25ff424d..5b83327e8 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -10,7 +10,7 @@ use core::ops::Index; use core::str::FromStr; use core::{fmt, slice}; -use hashes::{hash160, hash_newtype, sha512, GeneralHash, HashEngine, Hmac, HmacEngine}; +use hashes::{hash160, hash_newtype, sha512, HashEngine, Hmac, HmacEngine}; use internals::write_err; use secp256k1::{Secp256k1, XOnlyPublicKey}; @@ -584,9 +584,9 @@ impl From for Error { impl Xpriv { /// Constructs a new master key from a seed value pub fn new_master(network: impl Into, seed: &[u8]) -> Result { - let mut hmac_engine: HmacEngine = HmacEngine::new(b"Bitcoin seed"); - hmac_engine.input(seed); - let hmac_result: Hmac = Hmac::from_engine(hmac_engine); + let mut engine = HmacEngine::::new(b"Bitcoin seed"); + engine.input(seed); + let hmac = engine.finalize(); Ok(Xpriv { network: network.into(), @@ -594,9 +594,9 @@ impl Xpriv { parent_fingerprint: Default::default(), child_number: ChildNumber::ZERO_NORMAL, private_key: secp256k1::SecretKey::from_byte_array( - &hmac_result.as_ref()[..32].try_into().expect("Slice should be exactly 32 bytes"), + &hmac.as_ref()[..32].try_into().expect("Slice should be exactly 32 bytes"), )?, - chain_code: ChainCode::from_hmac(hmac_result), + chain_code: ChainCode::from_hmac(hmac), }) } @@ -650,25 +650,25 @@ impl Xpriv { /// Private->Private child key derivation fn ckd_priv(&self, secp: &Secp256k1, i: ChildNumber) -> Xpriv { - let mut hmac_engine: HmacEngine = HmacEngine::new(&self.chain_code[..]); + let mut engine = HmacEngine::::new(&self.chain_code[..]); match i { ChildNumber::Normal { .. } => { // Non-hardened key: compute public data and use that - hmac_engine.input( + engine.input( &secp256k1::PublicKey::from_secret_key(secp, &self.private_key).serialize()[..], ); } ChildNumber::Hardened { .. } => { // Hardened key: use only secret data to prevent public derivation - hmac_engine.input(&[0u8]); - hmac_engine.input(&self.private_key[..]); + engine.input(&[0u8]); + engine.input(&self.private_key[..]); } } - hmac_engine.input(&u32::from(i).to_be_bytes()); - let hmac_result: Hmac = Hmac::from_engine(hmac_engine); + engine.input(&u32::from(i).to_be_bytes()); + let hmac: Hmac = engine.finalize(); let sk = secp256k1::SecretKey::from_byte_array( - &hmac_result.as_ref()[..32].try_into().expect("statistically impossible to hit"), + &hmac.as_ref()[..32].try_into().expect("statistically impossible to hit"), ) .expect("statistically impossible to hit"); let tweaked = @@ -680,7 +680,7 @@ impl Xpriv { parent_fingerprint: self.fingerprint(secp), child_number: i, private_key: tweaked, - chain_code: ChainCode::from_hmac(hmac_result), + chain_code: ChainCode::from_hmac(hmac), } } @@ -812,18 +812,15 @@ impl Xpub { match i { ChildNumber::Hardened { .. } => Err(Error::CannotDeriveFromHardenedKey), ChildNumber::Normal { index: n } => { - let mut hmac_engine: HmacEngine = - HmacEngine::new(&self.chain_code[..]); - hmac_engine.input(&self.public_key.serialize()[..]); - hmac_engine.input(&n.to_be_bytes()); + let mut engine = HmacEngine::::new(&self.chain_code[..]); + engine.input(&self.public_key.serialize()[..]); + engine.input(&n.to_be_bytes()); - let hmac_result: Hmac = Hmac::from_engine(hmac_engine); + let hmac = engine.finalize(); let private_key = secp256k1::SecretKey::from_byte_array( - &hmac_result.as_ref()[..32] - .try_into() - .expect("Slice should be exactly 32 bytes"), + &hmac.as_ref()[..32].try_into().expect("Slice should be exactly 32 bytes"), )?; - let chain_code = ChainCode::from_hmac(hmac_result); + let chain_code = ChainCode::from_hmac(hmac); Ok((private_key, chain_code)) } } diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index e2946b16b..a8637c2c6 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -16,7 +16,7 @@ use core::mem; -use hashes::{sha256, sha256d, GeneralHash, Hash}; +use hashes::{sha256, sha256d, Hash}; use hex::DisplayHex as _; use internals::{compact_size, ToU64}; use io::{BufRead, Cursor, Read, Write}; @@ -629,7 +629,7 @@ impl Decodable for Box<[u8]> { /// Does a double-SHA256 on `data` and returns the first 4 bytes. fn sha2_checksum(data: &[u8]) -> [u8; 4] { - let checksum = ::hash(data); + let checksum = sha256d::hash(data); let checksum = checksum.to_byte_array(); [checksum[0], checksum[1], checksum[2], checksum[3]] } diff --git a/bitcoin/src/psbt/macros.rs b/bitcoin/src/psbt/macros.rs index 089d705b4..4044a5e52 100644 --- a/bitcoin/src/psbt/macros.rs +++ b/bitcoin/src/psbt/macros.rs @@ -113,15 +113,15 @@ macro_rules! impl_psbt_insert_pair { #[rustfmt::skip] macro_rules! psbt_insert_hash_pair { - (&mut $slf:ident.$map:ident <= $raw_key:ident|$raw_value:ident|$hash:path|$hash_type_error:path) => { + (&mut $slf:ident.$map:ident <= $raw_key:ident|$raw_value:ident|$hash:ident|$hash_type_error:path) => { if $raw_key.key_data.is_empty() { return Err($crate::psbt::Error::InvalidKey($raw_key)); } - let key_val: $hash = Deserialize::deserialize(&$raw_key.key_data)?; + let key_val: $hash::Hash = Deserialize::deserialize(&$raw_key.key_data)?; match $slf.$map.entry(key_val) { btree_map::Entry::Vacant(empty_key) => { let val: Vec = Deserialize::deserialize(&$raw_value)?; - if <$hash as hashes::GeneralHash>::hash(&val) != key_val { + if $hash::hash(&val) != key_val { return Err($crate::psbt::Error::InvalidPreimageHashPair { preimage: val.into_boxed_slice(), hash: Box::from(key_val.borrow()), diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs index 401bc34b4..66802623b 100644 --- a/bitcoin/src/psbt/map/input.rs +++ b/bitcoin/src/psbt/map/input.rs @@ -317,22 +317,22 @@ impl Input { } PSBT_IN_RIPEMD160 => { psbt_insert_hash_pair! { - &mut self.ripemd160_preimages <= raw_key|raw_value|ripemd160::Hash|error::PsbtHash::Ripemd + &mut self.ripemd160_preimages <= raw_key|raw_value|ripemd160|error::PsbtHash::Ripemd } } PSBT_IN_SHA256 => { psbt_insert_hash_pair! { - &mut self.sha256_preimages <= raw_key|raw_value|sha256::Hash|error::PsbtHash::Sha256 + &mut self.sha256_preimages <= raw_key|raw_value|sha256|error::PsbtHash::Sha256 } } PSBT_IN_HASH160 => { psbt_insert_hash_pair! { - &mut self.hash160_preimages <= raw_key|raw_value|hash160::Hash|error::PsbtHash::Hash160 + &mut self.hash160_preimages <= raw_key|raw_value|hash160|error::PsbtHash::Hash160 } } PSBT_IN_HASH256 => { psbt_insert_hash_pair! { - &mut self.hash256_preimages <= raw_key|raw_value|sha256d::Hash|error::PsbtHash::Hash256 + &mut self.hash256_preimages <= raw_key|raw_value|sha256d|error::PsbtHash::Hash256 } } PSBT_IN_TAP_KEY_SIG => { diff --git a/hashes/src/hash160/mod.rs b/hashes/src/hash160/mod.rs index 282d653d7..3c457c1aa 100644 --- a/hashes/src/hash160/mod.rs +++ b/hashes/src/hash160/mod.rs @@ -38,9 +38,13 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 20]; const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE; + fn input(&mut self, data: &[u8]) { self.0.input(data) } fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } #[cfg(test)] @@ -128,7 +132,7 @@ mod tests { mod benches { use test::Bencher; - use crate::{hash160, GeneralHash as _, Hash as _, HashEngine}; + use crate::{hash160, Hash as _, HashEngine}; #[bench] pub fn hash160_10(bh: &mut Bencher) { diff --git a/hashes/src/hkdf/mod.rs b/hashes/src/hkdf/mod.rs index f02e0dd47..c381422da 100644 --- a/hashes/src/hkdf/mod.rs +++ b/hashes/src/hkdf/mod.rs @@ -11,7 +11,7 @@ use alloc::vec; use alloc::vec::Vec; use core::fmt; -use crate::{GeneralHash, HashEngine, Hmac, HmacEngine, IsByteArray}; +use crate::{HashEngine, Hmac, HmacEngine, IsByteArray}; /// Output keying material max length multiple. const MAX_OUTPUT_BLOCKS: usize = 255; @@ -33,20 +33,20 @@ impl std::error::Error for MaxLengthError {} /// HMAC-based Extract-and-Expand Key Derivation Function (HKDF). #[derive(Copy, Clone)] -pub struct Hkdf { +pub struct Hkdf { /// Pseudorandom key based on the extract step. - prk: Hmac, + prk: Hmac, } -impl Hkdf +impl Hkdf where - ::Engine: Default, + T: Default, { /// Initialize a HKDF by performing the extract step. pub fn new(salt: &[u8], ikm: &[u8]) -> Self { - let mut hmac_engine: HmacEngine = HmacEngine::new(salt); - hmac_engine.input(ikm); - Self { prk: Hmac::from_engine(hmac_engine) } + let mut engine: HmacEngine = HmacEngine::new(salt); + engine.input(ikm); + Self { prk: engine.finalize() } } /// Expand the key to generate output key material in okm. @@ -65,19 +65,19 @@ where let total_blocks = (okm.len() + T::Bytes::LEN - 1) / T::Bytes::LEN; while counter <= total_blocks as u8 { - let mut hmac_engine: HmacEngine = HmacEngine::new(self.prk.as_ref()); + let mut engine: HmacEngine = HmacEngine::new(self.prk.as_ref()); // First block does not have a previous block, // all other blocks include last block in the HMAC input. if counter != 1u8 { let previous_start_index = (counter as usize - 2) * T::Bytes::LEN; let previous_end_index = (counter as usize - 1) * T::Bytes::LEN; - hmac_engine.input(&okm[previous_start_index..previous_end_index]); + engine.input(&okm[previous_start_index..previous_end_index]); } - hmac_engine.input(info); - hmac_engine.input(&[counter]); + engine.input(info); + engine.input(&[counter]); - let t = Hmac::from_engine(hmac_engine); + let t = engine.finalize(); let start_index = (counter as usize - 1) * T::Bytes::LEN; // Last block might not take full hash length. let end_index = if counter == (total_blocks as u8) { @@ -106,7 +106,7 @@ where } } -impl fmt::Debug for Hkdf { +impl fmt::Debug for Hkdf { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { use crate::{sha256t, sha256t_tag}; @@ -141,7 +141,7 @@ mod tests { let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); let info = Vec::from_hex("f0f1f2f3f4f5f6f7f8f9").unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let mut okm = [0u8; 42]; hkdf.expand(&info, &mut okm).unwrap(); @@ -163,7 +163,7 @@ mod tests { "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" ).unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let mut okm = [0u8; 82]; hkdf.expand(&info, &mut okm).unwrap(); @@ -179,7 +179,7 @@ mod tests { let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); let info = Vec::from_hex("f0f1f2f3f4f5f6f7f8f9").unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let mut okm = [0u8; 256 * 32]; let e = hkdf.expand(&info, &mut okm); @@ -192,7 +192,7 @@ mod tests { let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); let info = Vec::from_hex("f0f1f2f3f4f5f6f7f8f9").unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let mut okm = [0u8; 1]; hkdf.expand(&info, &mut okm).unwrap(); @@ -205,7 +205,7 @@ mod tests { let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); let info = Vec::from_hex("f0f1f2f3f4f5f6f7f8f9").unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let okm = hkdf.expand_to_len(&info, 42).unwrap(); assert_eq!( @@ -219,7 +219,7 @@ mod tests { let salt = Vec::from_hex("000102030405060708090a0b0c").unwrap(); let ikm = Vec::from_hex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(); - let hkdf = Hkdf::::new(&salt, &ikm); + let hkdf = Hkdf::::new(&salt, &ikm); let debug = alloc::format!("{:?}", hkdf); assert_eq!(debug, "Hkdf(#ec7bd36ab2ed4045)"); diff --git a/hashes/src/hmac/mod.rs b/hashes/src/hmac/mod.rs index 8215e48a7..2d70253fc 100644 --- a/hashes/src/hmac/mod.rs +++ b/hashes/src/hmac/mod.rs @@ -12,36 +12,26 @@ use core::{convert, fmt, str}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::{GeneralHash, Hash, HashEngine}; +use crate::{Hash, HashEngine}; /// A hash computed from a RFC 2104 HMAC. Parameterized by the underlying hash function. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct Hmac(T); +pub struct Hmac(T); -impl Hmac { - /// Constructs a new keyed HMAC engine from `key`. - pub fn engine(key: &[u8]) -> HmacEngine - where - ::Engine: Default, - { - HmacEngine::new(key) - } -} - -impl str::FromStr for Hmac { +impl str::FromStr for Hmac { type Err = ::Err; fn from_str(s: &str) -> Result { Ok(Hmac(str::FromStr::from_str(s)?)) } } /// Pair of underlying hash engines, used for the inner and outer hash of HMAC. #[derive(Debug, Clone)] -pub struct HmacEngine { - iengine: T::Engine, - oengine: T::Engine, +pub struct HmacEngine { + iengine: T, + oengine: T, } -impl HmacEngine { +impl HmacEngine { /// Constructs a new keyed HMAC engine from `key`. /// /// We only support underlying hashes whose block sizes are ≤ 128 bytes. @@ -51,24 +41,23 @@ impl HmacEngine { /// Larger hashes will result in a panic. pub fn new(key: &[u8]) -> HmacEngine where - ::Engine: Default, + T: Default, { - debug_assert!(T::Engine::BLOCK_SIZE <= 128); + debug_assert!(T::BLOCK_SIZE <= 128); let mut ipad = [0x36u8; 128]; let mut opad = [0x5cu8; 128]; - let mut ret = HmacEngine { - iengine: ::engine(), - oengine: ::engine(), - }; + let mut ret = HmacEngine { iengine: T::default(), oengine: T::default() }; - if key.len() > T::Engine::BLOCK_SIZE { - let hash = ::hash(key); - let hash = hash.as_byte_array().as_ref(); - for (b_i, b_h) in ipad.iter_mut().zip(hash) { + if key.len() > T::BLOCK_SIZE { + let mut engine = T::default(); + engine.input(key); + let hash = engine.finalize(); + + for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) { *b_i ^= *b_h; } - for (b_o, b_h) in opad.iter_mut().zip(hash) { + for (b_o, b_h) in opad.iter_mut().zip(hash.as_ref()) { *b_o ^= *b_h; } } else { @@ -80,54 +69,49 @@ impl HmacEngine { } }; - HashEngine::input(&mut ret.iengine, &ipad[..T::Engine::BLOCK_SIZE]); - HashEngine::input(&mut ret.oengine, &opad[..T::Engine::BLOCK_SIZE]); + ret.iengine.input(&ipad[..T::BLOCK_SIZE]); + ret.oengine.input(&opad[..T::BLOCK_SIZE]); ret } /// A special constructor giving direct access to the underlying "inner" and "outer" engines. - pub fn from_inner_engines(iengine: T::Engine, oengine: T::Engine) -> HmacEngine { + pub fn from_inner_engines(iengine: T, oengine: T) -> HmacEngine { HmacEngine { iengine, oengine } } } -impl HashEngine for HmacEngine { - const BLOCK_SIZE: usize = T::Engine::BLOCK_SIZE; +impl HashEngine for HmacEngine { + type Hash = Hmac; + type Bytes = T::Bytes; + const BLOCK_SIZE: usize = T::BLOCK_SIZE; fn n_bytes_hashed(&self) -> u64 { self.iengine.n_bytes_hashed() } - fn input(&mut self, buf: &[u8]) { self.iengine.input(buf) } + fn finalize(mut self) -> Self::Hash { + let ihash = self.iengine.finalize(); + self.oengine.input(ihash.as_ref()); + Hmac(self.oengine.finalize()) + } } -impl fmt::Debug for Hmac { +impl fmt::Debug for Hmac { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } } -impl fmt::Display for Hmac { +impl fmt::Display for Hmac { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } -impl fmt::LowerHex for Hmac { +impl fmt::LowerHex for Hmac { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) } } -impl convert::AsRef<[u8]> for Hmac { +impl convert::AsRef<[u8]> for Hmac { // Calling as_byte_array is more reliable fn as_ref(&self) -> &[u8] { self.0.as_byte_array().as_ref() } } -impl GeneralHash for Hmac { - type Engine = HmacEngine; - - fn from_engine(mut e: HmacEngine) -> Hmac { - let ihash = T::from_engine(e.iengine); - e.oengine.input(ihash.as_byte_array().as_ref()); - let ohash = T::from_engine(e.oengine); - Hmac(ohash) - } -} - -impl Hash for Hmac { +impl Hash for Hmac { type Bytes = T::Bytes; fn from_byte_array(bytes: T::Bytes) -> Self { Hmac(T::from_byte_array(bytes)) } @@ -143,14 +127,14 @@ impl Hash for Hmac { } #[cfg(feature = "serde")] -impl Serialize for Hmac { +impl Serialize for Hmac { fn serialize(&self, s: S) -> Result { Serialize::serialize(&self.0, s) } } #[cfg(feature = "serde")] -impl<'de, T: GeneralHash + Deserialize<'de>> Deserialize<'de> for Hmac { +impl<'de, T: Hash + Deserialize<'de>> Deserialize<'de> for Hmac { fn deserialize>(d: D) -> Result, D::Error> { let bytes = Deserialize::deserialize(d)?; Ok(Hmac(bytes)) @@ -165,14 +149,14 @@ crate::internal_macros::impl_write!( Ok(buf.len()) }, |_us| { Ok(()) }, - T: crate::GeneralHash + T: crate::HashEngine ); #[cfg(test)] mod tests { #[test] fn test() { - use crate::{sha256, GeneralHash as _, Hash as _, HashEngine, Hmac, HmacEngine}; + use crate::{sha256, Hash as _, HashEngine, HmacEngine}; #[derive(Clone)] struct Test { @@ -287,9 +271,9 @@ mod tests { ]; for test in tests { - let mut engine = HmacEngine::::new(test.key); + let mut engine = HmacEngine::::new(test.key); engine.input(test.input); - let hash = Hmac::::from_engine(engine); + let hash = engine.finalize(); assert_eq!(hash.as_ref(), test.output); assert_eq!(hash.to_byte_array(), test.output); } @@ -330,11 +314,11 @@ mod tests { mod benches { use test::Bencher; - use crate::{sha256, GeneralHash as _, HashEngine, Hmac}; + use crate::{sha256, HashEngine as _, HmacEngine}; #[bench] pub fn hmac_sha256_10(bh: &mut Bencher) { - let mut engine = Hmac::::engine(&[]); + let mut engine = HmacEngine::::new(&[]); let bytes = [1u8; 10]; bh.iter(|| { engine.input(&bytes); @@ -344,7 +328,7 @@ mod benches { #[bench] pub fn hmac_sha256_1k(bh: &mut Bencher) { - let mut engine = Hmac::::engine(&[]); + let mut engine = HmacEngine::::new(&[]); let bytes = [1u8; 1024]; bh.iter(|| { engine.input(&bytes); @@ -354,7 +338,7 @@ mod benches { #[bench] pub fn hmac_sha256_64k(bh: &mut Bencher) { - let mut engine = Hmac::::engine(&[]); + let mut engine = HmacEngine::::new(&[]); let bytes = [1u8; 65536]; bh.iter(|| { engine.input(&bytes); diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index fe5d6aa71..5e9599322 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -26,12 +26,6 @@ macro_rules! hash_trait_impls { #[cfg(not(feature = "hex"))] $crate::impl_debug_only!(Hash, { $bits / 8 }, $reverse $(, $gen: $gent)*); - impl<$($gen: $gent),*> $crate::GeneralHash for Hash<$($gen),*> { - type Engine = HashEngine<$($gen),*>; - - fn from_engine(e: Self::Engine) -> Hash<$($gen),*> { Self::from_engine(e) } - } - #[cfg(feature = "serde")] $crate::serde_impl!(Hash, { $bits / 8} $(, $gen: $gent)*); @@ -71,6 +65,30 @@ pub(crate) use hash_trait_impls; /// [`hash_trait_impls`]. macro_rules! general_hash_type { ($bits:expr, $reverse:expr, $doc:literal) => { + /// Hashes some bytes. + pub fn hash(data: &[u8]) -> Hash { + use crate::HashEngine as _; + + let mut engine = Hash::engine(); + engine.input(data); + engine.finalize() + } + + /// Hashes all the byte slices retrieved from the iterator together. + pub fn hash_byte_chunks(byte_slices: I) -> Hash + where + B: AsRef<[u8]>, + I: IntoIterator, + { + use crate::HashEngine as _; + + let mut engine = Hash::engine(); + for slice in byte_slices { + engine.input(slice.as_ref()); + } + engine.finalize() + } + $crate::internal_macros::hash_type_no_default!($bits, $reverse, $doc); impl Hash { @@ -82,7 +100,7 @@ macro_rules! general_hash_type { /// Hashes some bytes. #[allow(clippy::self_named_constructors)] // Hash is a noun and a verb. - pub fn hash(data: &[u8]) -> Self { ::hash(data) } + pub fn hash(data: &[u8]) -> Self { hash(data) } /// Hashes all the byte slices retrieved from the iterator together. pub fn hash_byte_chunks(byte_slices: I) -> Self @@ -90,7 +108,7 @@ macro_rules! general_hash_type { B: AsRef<[u8]>, I: IntoIterator, { - ::hash_byte_chunks(byte_slices) + hash_byte_chunks(byte_slices) } } }; diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 06cfbcda3..b3962214c 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -186,6 +186,15 @@ pub type HkdfSha512 = Hkdf; /// A hashing engine which bytes can be serialized into. pub trait HashEngine: Clone { + /// The `Hash` type returned when finalizing this engine. + type Hash: Hash; + + /// The byte array that is used internally in `finalize`. + type Bytes: Copy + IsByteArray; + + /// Length of the hash, in bytes. + const LEN: usize = Self::Bytes::LEN; + /// Length of the hash's internal block size, in bytes. const BLOCK_SIZE: usize; @@ -194,52 +203,9 @@ pub trait HashEngine: Clone { /// Return the number of bytes already input into the engine. fn n_bytes_hashed(&self) -> u64; -} -/// Trait describing hash digests which can be constructed by hashing arbitrary data. -/// -/// Some methods have been bound to engines which implement Default, which is -/// generally an unkeyed hash function. -pub trait GeneralHash: Hash { - /// A hashing engine which bytes can be serialized into. It is expected - /// to implement the `io::Write` trait, and to never return errors under - /// any conditions. - type Engine: HashEngine; - - /// Constructs a new engine. - fn engine() -> Self::Engine - where - Self::Engine: Default, - { - Self::Engine::default() - } - - /// Produces a hash from the current state of a given engine. - fn from_engine(e: Self::Engine) -> Self; - - /// Hashes some bytes. - fn hash(data: &[u8]) -> Self - where - Self::Engine: Default, - { - let mut engine = Self::engine(); - engine.input(data); - Self::from_engine(engine) - } - - /// Hashes all the byte slices retrieved from the iterator together. - fn hash_byte_chunks(byte_slices: I) -> Self - where - B: AsRef<[u8]>, - I: IntoIterator, - Self::Engine: Default, - { - let mut engine = Self::engine(); - for slice in byte_slices { - engine.input(slice.as_ref()); - } - Self::from_engine(engine) - } + /// Finalizes this engine. + fn finalize(self) -> Self::Hash; } /// Trait which applies to hashes of all types. diff --git a/hashes/src/ripemd160/mod.rs b/hashes/src/ripemd160/mod.rs index e6be17537..d6ec93f9d 100644 --- a/hashes/src/ripemd160/mod.rs +++ b/hashes/src/ripemd160/mod.rs @@ -87,9 +87,11 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 20]; const BLOCK_SIZE: usize = 64; fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed } - crate::internal_macros::engine_input_impl!(); + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } diff --git a/hashes/src/sha1/mod.rs b/hashes/src/sha1/mod.rs index dc32d31dd..18f620253 100644 --- a/hashes/src/sha1/mod.rs +++ b/hashes/src/sha1/mod.rs @@ -79,9 +79,13 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 20]; const BLOCK_SIZE: usize = 64; fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed } crate::internal_macros::engine_input_impl!(); + + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } diff --git a/hashes/src/sha256/mod.rs b/hashes/src/sha256/mod.rs index 6ae83b879..5d8fe125b 100644 --- a/hashes/src/sha256/mod.rs +++ b/hashes/src/sha256/mod.rs @@ -128,19 +128,19 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 32]; const BLOCK_SIZE: usize = 64; fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed } - crate::internal_macros::engine_input_impl!(); + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } impl Hash { /// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash #[must_use] - pub fn hash_again(&self) -> sha256d::Hash { - crate::Hash::from_byte_array(::hash(&self.0).0) - } + pub fn hash_again(&self) -> sha256d::Hash { sha256d::Hash::from_byte_array(hash(&self.0).0) } /// Computes hash from `bytes` in `const` context. /// diff --git a/hashes/src/sha256d/mod.rs b/hashes/src/sha256d/mod.rs index adb2ffa71..69bb8a72a 100644 --- a/hashes/src/sha256d/mod.rs +++ b/hashes/src/sha256d/mod.rs @@ -33,9 +33,13 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 32]; const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE; + fn input(&mut self, data: &[u8]) { self.0.input(data) } fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } #[cfg(test)] diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index 19a6ef768..d5acd5edf 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -9,6 +9,34 @@ use core::marker::PhantomData; use crate::sha256::Midstate; use crate::{sha256, HashEngine as _}; +/// Hashes some bytes. +pub fn hash(data: &[u8]) -> Hash +where + T: Tag, +{ + use crate::HashEngine as _; + + let mut engine = HashEngine::default(); + engine.input(data); + engine.finalize() +} + +/// Hashes all the byte slices retrieved from the iterator together. +pub fn hash_byte_chunks(byte_slices: I) -> Hash +where + B: AsRef<[u8]>, + I: IntoIterator, + T: Tag, +{ + use crate::HashEngine as _; + + let mut engine = HashEngine::default(); + for slice in byte_slices { + engine.input(slice.as_ref()); + } + engine.finalize() +} + /// Trait representing a tag that can be used as a context for SHA256t hashes. pub trait Tag { /// The [`Midstate`] after pre-tagging the hash engine. @@ -133,9 +161,13 @@ impl Clone for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 32]; const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE; + fn input(&mut self, data: &[u8]) { self.0.input(data) } fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } crate::internal_macros::impl_write!( diff --git a/hashes/src/sha384/mod.rs b/hashes/src/sha384/mod.rs index c20b356dc..3d6538af0 100644 --- a/hashes/src/sha384/mod.rs +++ b/hashes/src/sha384/mod.rs @@ -30,11 +30,13 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 48]; const BLOCK_SIZE: usize = sha512::BLOCK_SIZE; fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } - fn input(&mut self, inp: &[u8]) { self.0.input(inp); } + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } #[cfg(test)] diff --git a/hashes/src/sha512/mod.rs b/hashes/src/sha512/mod.rs index 65bf75c18..0256cde21 100644 --- a/hashes/src/sha512/mod.rs +++ b/hashes/src/sha512/mod.rs @@ -120,9 +120,11 @@ impl HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 64]; const BLOCK_SIZE: usize = 128; fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed } - crate::internal_macros::engine_input_impl!(); + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } diff --git a/hashes/src/sha512_256/mod.rs b/hashes/src/sha512_256/mod.rs index 40b4ca651..cf12d4604 100644 --- a/hashes/src/sha512_256/mod.rs +++ b/hashes/src/sha512_256/mod.rs @@ -40,11 +40,13 @@ impl Default for HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 64]; const BLOCK_SIZE: usize = sha512::BLOCK_SIZE; fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } - fn input(&mut self, inp: &[u8]) { self.0.input(inp); } + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } #[cfg(test)] diff --git a/hashes/src/siphash24/mod.rs b/hashes/src/siphash24/mod.rs index 1db747733..50abe8575 100644 --- a/hashes/src/siphash24/mod.rs +++ b/hashes/src/siphash24/mod.rs @@ -121,6 +121,8 @@ impl HashEngine { } impl crate::HashEngine for HashEngine { + type Hash = Hash; + type Bytes = [u8; 8]; const BLOCK_SIZE: usize = 8; #[inline] @@ -165,6 +167,8 @@ impl crate::HashEngine for HashEngine { } fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed } + + fn finalize(self) -> Self::Hash { Hash::from_engine(self) } } impl Hash { diff --git a/hashes/tests/regression.rs b/hashes/tests/regression.rs index 319d6ecf3..31aeec5f8 100644 --- a/hashes/tests/regression.rs +++ b/hashes/tests/regression.rs @@ -8,7 +8,7 @@ use bitcoin_hashes::{ hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24, - GeneralHash as _, HashEngine as _, Hmac, HmacEngine, + HashEngine as _, HmacEngine, }; const DATA: &str = "arbitrary data to hash as a regression test"; @@ -57,9 +57,9 @@ fn regression_sha256t() { #[test] fn regression_hmac_sha256_with_key() { - let mut engine = HmacEngine::::new(HMAC_KEY); + let mut engine = HmacEngine::::new(HMAC_KEY); engine.input(DATA.as_bytes()); - let hash = Hmac::from_engine(engine); + let hash = engine.finalize(); let got = format!("{}", hash); let want = "d159cecaf4adf90b6a641bab767e4817d3a51c414acea3682686c35ec0b37b52"; @@ -68,9 +68,9 @@ fn regression_hmac_sha256_with_key() { #[test] fn regression_hmac_sha512_with_key() { - let mut engine = HmacEngine::::new(HMAC_KEY); + let mut engine = HmacEngine::::new(HMAC_KEY); engine.input(DATA.as_bytes()); - let hash = Hmac::from_engine(engine); + let hash = engine.finalize(); let got = format!("{}", hash); let want = "8511773748f89ba22c07fb3a2981a12c1823695119de41f4a62aead6b848bd34939acf16475c35ed7956114fead3e794cc162ecd35e447a4dabc3227d55f757b"; @@ -87,3 +87,13 @@ fn regression_siphash24_with_key() { let want = "e823ed82311d601a"; assert_eq!(got, want); } + +#[test] +fn regression_sha256_hash_again() { + let hash = sha256::Hash::hash(b"Don't explain your philosophy. Embody it."); + let again = hash.hash_again(); + + let got = format!("{}", again); + let want = "28273103bcd88ab99e2b1007174770ff3f0ea91ee4b3ac942879ed1a2d264b4c"; + assert_eq!(got, want); +} diff --git a/io/src/hash.rs b/io/src/hash.rs index a6a364e94..859f035b7 100644 --- a/io/src/hash.rs +++ b/io/src/hash.rs @@ -10,9 +10,11 @@ use hashes::hmac::HmacEngine; use hashes::{ hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24, - GeneralHash, HashEngine as _, + HashEngine as _, }; +use crate::BufRead; + macro_rules! impl_write { ($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => { // `std::io::Write` is implemented in `bitcoin_hashes` because of the orphan rule. @@ -128,43 +130,28 @@ impl_write!( Ok(buf.len()) }, |_us| { Ok(()) }, - T: hashes::GeneralHash + T: hashes::HashEngine ); /// Hashes data from a reader. -/// -/// Adds functionality to a [`hashes::GeneralHash`] type to support hashing data read from a -/// buffered reader. -pub trait GeneralHashExt: GeneralHash + sealed::Sealed { - /// Hashes the entire contents of the `reader`. - fn hash_reader(reader: &mut R) -> Result - where - Self::Engine: Default, - { - let mut engine = Self::engine(); // This calls `Self::Engine::default()`. - loop { - let bytes = reader.fill_buf()?; +pub fn hash_reader(reader: &mut impl BufRead) -> Result +where + T: hashes::HashEngine + Default, +{ + let mut engine = T::default(); + loop { + let bytes = reader.fill_buf()?; - let read = bytes.len(); - // Empty slice means EOF. - if read == 0 { - break; - } - - engine.input(bytes); - reader.consume(read); + let read = bytes.len(); + // Empty slice means EOF. + if read == 0 { + break; } - Ok(Self::from_engine(engine)) + + engine.input(bytes); + reader.consume(read); } -} - -impl GeneralHashExt for T {} - -mod sealed { - /// Used to seal the `GeneralHashExt` trait. - pub trait Sealed {} - - impl Sealed for T {} + Ok(engine.finalize()) } #[cfg(test)] @@ -172,7 +159,7 @@ mod sealed { mod tests { use alloc::format; - use hashes::{hmac, Hmac}; + use hashes::hmac; use super::*; use crate::{Cursor, Write as _}; @@ -257,24 +244,24 @@ mod tests { #[test] fn hmac() { - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); engine.write_all(&[]).unwrap(); assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), + format!("{}", engine.finalize()), "bf5515149cf797955c4d3194cca42472883281951697c8375d9d9b107f384225" ); - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); engine.write_all(&[1; 256]).unwrap(); assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), + format!("{}", engine.finalize()), "59c9aca10c81c73cb4c196d94db741b6bf2050e0153d5a45f2526bff34675ac5" ); - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); engine.write_all(&[99; 64000]).unwrap(); assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), + format!("{}", engine.finalize()), "30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08" ); } @@ -308,7 +295,7 @@ mod tests { assert_eq!(got, $want); let mut reader = Cursor::new(DATA); - let hash_from_reader = $module::Hash::hash_reader(&mut reader).unwrap(); + let hash_from_reader = $crate::hash_reader::<$module::HashEngine>(&mut reader).unwrap(); assert_eq!(hash_from_reader, hash) } )* @@ -345,9 +332,9 @@ mod tests { #[test] fn regression_hmac_sha256_with_key() { - let mut engine = HmacEngine::::new(HMAC_KEY); + let mut engine = HmacEngine::::new(HMAC_KEY); engine.input(DATA.as_bytes()); - let hash = Hmac::from_engine(engine); + let hash = engine.finalize(); let got = format!("{}", hash); let want = "d159cecaf4adf90b6a641bab767e4817d3a51c414acea3682686c35ec0b37b52"; @@ -356,9 +343,9 @@ mod tests { #[test] fn regression_hmac_sha512_with_key() { - let mut engine = HmacEngine::::new(HMAC_KEY); + let mut engine = HmacEngine::::new(HMAC_KEY); engine.input(DATA.as_bytes()); - let hash = Hmac::from_engine(engine); + let hash = engine.finalize(); let got = format!("{}", hash); let want = "8511773748f89ba22c07fb3a2981a12c1823695119de41f4a62aead6b848bd34939acf16475c35ed7956114fead3e794cc162ecd35e447a4dabc3227d55f757b"; diff --git a/io/src/lib.rs b/io/src/lib.rs index 0b7ee5093..8da2d108d 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -46,7 +46,7 @@ pub use bridge::{FromStd, ToStd}; #[rustfmt::skip] // Keep public re-exports separate. pub use self::error::{Error, ErrorKind}; #[cfg(feature = "hashes")] -pub use self::hash::GeneralHashExt; +pub use self::hash::hash_reader; /// Result type returned by functions in this crate. pub type Result = core::result::Result;