diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 5e47891e0..f90f321d5 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -98,6 +98,7 @@ name = "bitcoin-io" version = "0.2.0" dependencies = [ "bitcoin-internals", + "bitcoin_hashes 0.16.0", ] [[package]] @@ -107,7 +108,6 @@ dependencies = [ "arbitrary", "bincode", "bitcoin-internals", - "bitcoin-io", "bitcoin-units", "bitcoin_hashes 0.16.0", "hex-conservative 0.3.0", @@ -141,7 +141,6 @@ dependencies = [ name = "bitcoin_hashes" version = "0.16.0" dependencies = [ - "bitcoin-io", "hex-conservative 0.3.0", "serde", "serde_json", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 47641c992..8b590d8ac 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -97,6 +97,7 @@ name = "bitcoin-io" version = "0.2.0" dependencies = [ "bitcoin-internals", + "bitcoin_hashes 0.16.0", ] [[package]] @@ -106,7 +107,6 @@ dependencies = [ "arbitrary", "bincode", "bitcoin-internals", - "bitcoin-io", "bitcoin-units", "bitcoin_hashes 0.16.0", "hex-conservative 0.3.0", @@ -140,7 +140,6 @@ dependencies = [ name = "bitcoin_hashes" version = "0.16.0" dependencies = [ - "bitcoin-io", "hex-conservative 0.3.0", "serde", "serde_json", diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index cf0575029..9713bf200 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -27,10 +27,10 @@ arbitrary = ["dep:arbitrary", "units/arbitrary", "primitives/arbitrary"] [dependencies] base58 = { package = "base58ck", version = "0.2.0", default-features = false, features = ["alloc"] } bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] } -hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, features = ["alloc", "bitcoin-io", "hex"] } +hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, features = ["alloc", "hex"] } hex = { package = "hex-conservative", version = "0.3.0", default-features = false, features = ["alloc"] } internals = { package = "bitcoin-internals", version = "0.4.0", features = ["alloc"] } -io = { package = "bitcoin-io", version = "0.2.0", default-features = false, features = ["alloc"] } +io = { package = "bitcoin-io", version = "0.2.0", default-features = false, features = ["alloc", "hashes"] } primitives = { package = "bitcoin-primitives", version = "0.101.0", default-features = false, features = ["alloc"] } secp256k1 = { version = "0.29.0", default-features = false, features = ["hashes", "alloc"] } units = { package = "bitcoin-units", version = "0.2.0", default-features = false, features = ["alloc"] } diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml index c34f2f04e..35760c87e 100644 --- a/hashes/Cargo.toml +++ b/hashes/Cargo.toml @@ -15,8 +15,8 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["alloc", "bitcoin-io?/std", "hex/std"] -alloc = ["bitcoin-io?/alloc", "hex/alloc"] +std = ["alloc", "hex/std"] +alloc = ["hex/alloc"] serde = ["dep:serde", "hex"] # Smaller (but slower) implementation of sha256, sha512 and ripemd160 small-hash = [] @@ -24,7 +24,6 @@ small-hash = [] [dependencies] hex = { package = "hex-conservative", version = "0.3.0", default-features = false, optional = true } -bitcoin-io = { version = "0.2.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } [dev-dependencies] diff --git a/hashes/contrib/sanitizer.sh b/hashes/contrib/sanitizer.sh index e3d1e8152..495e404c1 100755 --- a/hashes/contrib/sanitizer.sh +++ b/hashes/contrib/sanitizer.sh @@ -5,7 +5,7 @@ set -euox pipefail # Run the sanitizer with these features. -FEATURES="std bitcoin-io serde" +FEATURES="std serde" cargo clean CC='clang -fsanitize=address -fno-omit-frame-pointer' \ diff --git a/hashes/contrib/test_vars.sh b/hashes/contrib/test_vars.sh index 35360740a..efcd3dcfc 100644 --- a/hashes/contrib/test_vars.sh +++ b/hashes/contrib/test_vars.sh @@ -5,10 +5,10 @@ # shellcheck disable=SC2034 # Test all these features with "std" enabled. -FEATURES_WITH_STD="bitcoin-io serde small-hash" +FEATURES_WITH_STD="serde small-hash" # Test all these features without "std" enabled. -FEATURES_WITHOUT_STD="alloc bitcoin-io serde small-hash" +FEATURES_WITHOUT_STD="alloc serde small-hash" # Run these examples. EXAMPLES="" diff --git a/hashes/embedded/Cargo.toml b/hashes/embedded/Cargo.toml index be73fe563..dcf5a7ecb 100644 --- a/hashes/embedded/Cargo.toml +++ b/hashes/embedded/Cargo.toml @@ -19,8 +19,8 @@ cortex-m-rt = "0.6.10" cortex-m-semihosting = "0.3.3" panic-halt = "0.2.0" alloc-cortex-m = { version = "0.4.1", optional = true } -bitcoin_hashes = { path="../", default-features = false, features = ["bitcoin-io"] } -bitcoin-io = { path = "../../io", default_features = false } +bitcoin_hashes = { path="../", default-features = false, features = [] } +bitcoin-io = { path = "../../io", default_features = false, features = ["hashes"] } [[bin]] name = "embedded" @@ -32,6 +32,9 @@ codegen-units = 1 # better optimizations debug = true # symbols are nice and they don't increase the size on Flash lto = true # better optimizations +[patch.crates-io.bitcoin_hashes] +path = "../../hashes" + [patch.crates-io.bitcoin-internals] path = "../../internals" diff --git a/hashes/src/hmac/mod.rs b/hashes/src/hmac/mod.rs index 5cddfdc53..eace7f951 100644 --- a/hashes/src/hmac/mod.rs +++ b/hashes/src/hmac/mod.rs @@ -155,7 +155,8 @@ impl<'de, T: GeneralHash + Deserialize<'de>> Deserialize<'de> for Hmac { } } -crate::internal_macros::impl_io_write!( +#[cfg(feature = "std")] +crate::internal_macros::impl_write!( HmacEngine, |us: &mut HmacEngine, buf| { us.input(buf); diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 5ae79f119..67b9f6170 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -91,12 +91,6 @@ macro_rules! general_hash_type { { ::hash_byte_chunks(byte_slices) } - - /// Hashes the entire contents of the `reader`. - #[cfg(feature = "bitcoin-io")] - pub fn hash_reader(reader: &mut R) -> Result { - ::hash_reader(reader) - } } }; } @@ -156,7 +150,7 @@ macro_rules! hash_type_no_default { $crate::internal_macros::hash_trait_impls!($bits, $reverse); - $crate::internal_macros::impl_io_write!( + $crate::internal_macros::impl_write!( HashEngine, |us: &mut HashEngine, buf| { crate::HashEngine::input(us, buf); @@ -168,29 +162,16 @@ macro_rules! hash_type_no_default { } pub(crate) use hash_type_no_default; -// We do not use the `bitcoin_io::impl_write` macro because we don't have an unconditional -// dependency on `bitcoin-io` and we want to implement `std:io::Write` even when we don't depend on -// `bitcoin-io`. -macro_rules! impl_io_write { +macro_rules! impl_write { ($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => { - #[cfg(feature = "bitcoin-io")] - impl<$($bounded_ty: $bounds),*> bitcoin_io::Write for $ty { - #[inline] - fn write(&mut self, buf: &[u8]) -> bitcoin_io::Result { - $write_fn(self, buf) - } - #[inline] - fn flush(&mut self) -> bitcoin_io::Result<()> { - $flush_fn(self) - } - } - + // `bitcoin_io::Write` is implemented in `bitcoin_io`. #[cfg(feature = "std")] impl<$($bounded_ty: $bounds),*> std::io::Write for $ty { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { $write_fn(self, buf) } + #[inline] fn flush(&mut self) -> std::io::Result<()> { $flush_fn(self) @@ -198,7 +179,7 @@ macro_rules! impl_io_write { } } } -pub(crate) use impl_io_write; +pub(crate) use impl_write; macro_rules! engine_input_impl( () => ( diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 9f0067aaf..0324da01a 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -75,9 +75,6 @@ extern crate core; #[cfg(feature = "std")] extern crate std; -#[cfg(feature = "bitcoin-io")] -extern crate bitcoin_io as io; - /// A generic serialization/deserialization framework. #[cfg(feature = "serde")] pub extern crate serde; @@ -239,28 +236,6 @@ pub trait GeneralHash: Hash { } Self::from_engine(engine) } - - /// Hashes the entire contents of the `reader`. - #[cfg(feature = "bitcoin-io")] - fn hash_reader(reader: &mut R) -> Result - where - Self::Engine: Default, - { - let mut engine = Self::engine(); - loop { - let bytes = reader.fill_buf()?; - - let read = bytes.len(); - // Empty slice means EOF. - if read == 0 { - break; - } - - engine.input(bytes); - reader.consume(read); - } - Ok(Self::from_engine(engine)) - } } /// Trait which applies to hashes of all types. @@ -367,13 +342,4 @@ mod tests { let rinsed = hex.parse::().expect("failed to parse hex"); assert_eq!(rinsed, orig) } - - #[test] - #[cfg(feature = "bitcoin-io")] - fn hash_reader() { - use crate::sha256; - - let mut reader: &[u8] = b"hello"; - assert_eq!(sha256::Hash::hash_reader(&mut reader).unwrap(), sha256::Hash::hash(b"hello"),) - } } diff --git a/hashes/src/sha256t/mod.rs b/hashes/src/sha256t/mod.rs index 5d28b9d93..e2461eebf 100644 --- a/hashes/src/sha256t/mod.rs +++ b/hashes/src/sha256t/mod.rs @@ -87,25 +87,6 @@ where Self::from_engine(engine) } - /// Hashes the entire contents of the `reader`. - #[cfg(feature = "bitcoin-io")] - pub fn hash_reader(reader: &mut R) -> Result { - let mut engine = Self::engine(); - loop { - let bytes = reader.fill_buf()?; - - let read = bytes.len(); - // Empty slice means EOF. - if read == 0 { - break; - } - - engine.input(bytes); - reader.consume(read); - } - Ok(Self::from_engine(engine)) - } - /// Returns the underlying byte array. pub const fn to_byte_array(self) -> [u8; 32] { self.0 } @@ -152,7 +133,7 @@ impl crate::HashEngine for HashEngine { fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() } } -crate::internal_macros::impl_io_write!( +crate::internal_macros::impl_write!( HashEngine, |us: &mut HashEngine, buf| { us.input(buf); diff --git a/hashes/tests/io.rs b/hashes/tests/io.rs deleted file mode 100644 index 9151bba30..000000000 --- a/hashes/tests/io.rs +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: CC0-1.0 - -//! Test the `bitcoin-io` implementations. - -#![cfg(feature = "bitcoin-io")] -#![cfg(feature = "hex")] - -use bitcoin_hashes::{ - hash160, hmac, ripemd160, sha1, sha256, sha256d, sha384, sha512, sha512_256, siphash24, - GeneralHash as _, -}; -use bitcoin_io::Write; - -macro_rules! write_test { - ($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => { - #[test] - fn $mod() { - let mut engine = $mod::Hash::engine(); - engine.write_all(&[]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_empty); - - let mut engine = $mod::Hash::engine(); - engine.write_all(&[1; 256]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_256); - - let mut engine = $mod::Hash::engine(); - engine.write_all(&[99; 64000]).unwrap(); - assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_64k); - } - }; -} - -write_test!( - sha1, - "da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ac458b067c6b021c7e9358229b636e9d1e4cb154", - "e4b66838f9f7b6f91e5be32a02ae78094df402e7", -); - -write_test!( - sha256, - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - "2661920f2409dd6c8adeb0c44972959f232b6429afa913845d0fd95e7e768234", - "5c5e904f5d4fd587c7a906bf846e08a927286f388c54c39213a4884695271bbc", -); - -write_test!( - sha256d, - "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", - "374000d830c75d10d9417e493a7652920f30efbd300e3fb092f24c28c20baf64", - "0050d4148ad7a0437ca0643fad5bf4614cd95d9ba21fde52370b37dcc3f03307", -); - -write_test!( - sha384, - "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", - "82135637ef6d6dd31a20e2bc9998681a3eecaf8f8c76d45e545214de38439d9a533848ec75f53e4b1a8805709c5124d0", - "fb7511d9a98c5686f9c2f55e242397815c9229d8759451e1710b8da6861e08d52f0357176f4b74f8cad9e23ab65411c7", -); - -write_test!( - sha512, - "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\ - 47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", - "57ecf739d3a7ca647639adae80a05f4f361304bfcbfa1ceba93296b096e74287\ - 45fc10c142cecdd3bb587a3dba598c072f6f78b31cc0a06a3da0105ee51f75d6", - "dd28f78c53f3bc9bd0c2dca9642a1ad402a70412f985c1f6e54fadb98ce9c458\ - 4761df8d04ed04bb734ba48dd2106bb9ea54524f1394cdd18e6da3166e71c3ee", -); - -write_test!( - sha512_256, - "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a", - "8d4bb96e7956cf5f08bf5c45f7982630c46b0b022f25cbaf722ae97c06a6e7a2", - "3367646f3e264653f7dd664ac2cb6d3b96329e86ffb7a29a1082e2a4ddc9ee7a", -); - -write_test!( - ripemd160, - "9c1185a5c5e9fc54612808977ee8f548b2258d31", - "e571a1ca5b780aa52bafdb9ec852544ffca418ba", - "ddd2ecce739e823629c7d46ab18918e9c4a51c75", -); - -write_test!( - hash160, - "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", - "671356a1a874695ad3bc20cae440f4360835bd5a", - "a9608c952c8dbcc20c53803d2ca5ad31d64d9313", -); - -#[test] -fn hmac() { - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "bf5515149cf797955c4d3194cca42472883281951697c8375d9d9b107f384225" - ); - - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[1; 256]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "59c9aca10c81c73cb4c196d94db741b6bf2050e0153d5a45f2526bff34675ac5" - ); - - let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); - engine.write_all(&[99; 64000]).unwrap(); - assert_eq!( - format!("{}", hmac::Hmac::from_engine(engine)), - "30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08" - ); -} - -#[test] -fn siphash24() { - let mut engine = siphash24::HashEngine::with_keys(0, 0); - engine.write_all(&[]).unwrap(); - assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "d70077739d4b921e"); - - let mut engine = siphash24::HashEngine::with_keys(0, 0); - engine.write_all(&[1; 256]).unwrap(); - assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "3a3ccefde9b5b1e3"); - - let mut engine = siphash24::HashEngine::with_keys(0, 0); - engine.write_all(&[99; 64000]).unwrap(); - assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "ce456e4e4ecbc5bf"); -} diff --git a/hashes/tests/regression.rs b/hashes/tests/regression.rs index a4ffddc2f..319d6ecf3 100644 --- a/hashes/tests/regression.rs +++ b/hashes/tests/regression.rs @@ -1,6 +1,8 @@ //! Regression tests for each hash type. //! //! Note that if `bitcoin-io` is enabled then we get more regression-like testing from `./io.rs`. +//! +//! Test input data and expected hashes is the same as in `io/src/hash.rs`. #![cfg(feature = "hex")] diff --git a/io/Cargo.toml b/io/Cargo.toml index 224d37804..d245121da 100644 --- a/io/Cargo.toml +++ b/io/Cargo.toml @@ -15,12 +15,14 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["alloc"] -alloc = [] +std = ["alloc", "hashes?/std"] +alloc = ["hashes?/alloc"] [dependencies] internals = { package = "bitcoin-internals", version = "0.4.0" } +hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, optional = true } + [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] diff --git a/io/contrib/test_vars.sh b/io/contrib/test_vars.sh index 92dc09406..279c2507c 100644 --- a/io/contrib/test_vars.sh +++ b/io/contrib/test_vars.sh @@ -5,10 +5,10 @@ # shellcheck disable=SC2034 # Test all these features with "std" enabled. -FEATURES_WITH_STD="" +FEATURES_WITH_STD="hashes" # Test all these features without "std" enabled. -FEATURES_WITHOUT_STD="alloc" +FEATURES_WITHOUT_STD="alloc hashes" # Run these examples. EXAMPLES="" diff --git a/io/src/hash.rs b/io/src/hash.rs new file mode 100644 index 000000000..a6a364e94 --- /dev/null +++ b/io/src/hash.rs @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: CC0-1.0 + +//! I/O hashing support. +//! +//! Support for various hashing related things e.g. +//! +//! - Hashing to a writer. +//! - Implement I/O traits for hash engines. + +use hashes::hmac::HmacEngine; +use hashes::{ + hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24, + GeneralHash, HashEngine as _, +}; + +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. + impl<$($bounded_ty: $bounds),*> crate::Write for $ty { + #[inline] + fn write(&mut self, buf: &[u8]) -> crate::Result { + $write_fn(self, buf)} + + #[inline] + fn flush(&mut self) -> crate::Result<()> { + $flush_fn(self) + } + } + } +} +pub(crate) use impl_write; + +impl_write!( + hash160::HashEngine, + |us: &mut hash160::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + ripemd160::HashEngine, + |us: &mut ripemd160::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha1::HashEngine, + |us: &mut sha1::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha256::HashEngine, + |us: &mut sha256::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha256d::HashEngine, + |us: &mut sha256d::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha256t::HashEngine, + |us: &mut sha256t::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) }, + T: sha256t::Tag +); + +impl_write!( + sha384::HashEngine, + |us: &mut sha384::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha512::HashEngine, + |us: &mut sha512::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + sha512_256::HashEngine, + |us: &mut sha512_256::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + siphash24::HashEngine, + |us: &mut siphash24::HashEngine, buf| { + hashes::HashEngine::input(us, buf); + Ok(buf.len()) + }, + |_us| { Ok(()) } +); + +impl_write!( + HmacEngine, + |us: &mut HmacEngine, buf| { + us.input(buf); + Ok(buf.len()) + }, + |_us| { Ok(()) }, + T: hashes::GeneralHash +); + +/// 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()?; + + let read = bytes.len(); + // Empty slice means EOF. + if read == 0 { + break; + } + + engine.input(bytes); + reader.consume(read); + } + Ok(Self::from_engine(engine)) + } +} + +impl GeneralHashExt for T {} + +mod sealed { + /// Used to seal the `GeneralHashExt` trait. + pub trait Sealed {} + + impl Sealed for T {} +} + +#[cfg(test)] +#[cfg(feature = "alloc")] +mod tests { + use alloc::format; + + use hashes::{hmac, Hmac}; + + use super::*; + use crate::{Cursor, Write as _}; + + macro_rules! write_test { + ($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => { + #[test] + fn $mod() { + let mut engine = $mod::Hash::engine(); + engine.write_all(&[]).unwrap(); + assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_empty); + + let mut engine = $mod::Hash::engine(); + engine.write_all(&[1; 256]).unwrap(); + assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_256); + + let mut engine = $mod::Hash::engine(); + engine.write_all(&[99; 64000]).unwrap(); + assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_64k); + } + }; + } + + write_test!( + sha1, + "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "ac458b067c6b021c7e9358229b636e9d1e4cb154", + "e4b66838f9f7b6f91e5be32a02ae78094df402e7", + ); + + write_test!( + sha256, + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "2661920f2409dd6c8adeb0c44972959f232b6429afa913845d0fd95e7e768234", + "5c5e904f5d4fd587c7a906bf846e08a927286f388c54c39213a4884695271bbc", + ); + + write_test!( + sha256d, + "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d", + "374000d830c75d10d9417e493a7652920f30efbd300e3fb092f24c28c20baf64", + "0050d4148ad7a0437ca0643fad5bf4614cd95d9ba21fde52370b37dcc3f03307", + ); + + write_test!( + sha384, + "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", + "82135637ef6d6dd31a20e2bc9998681a3eecaf8f8c76d45e545214de38439d9a533848ec75f53e4b1a8805709c5124d0", + "fb7511d9a98c5686f9c2f55e242397815c9229d8759451e1710b8da6861e08d52f0357176f4b74f8cad9e23ab65411c7", + ); + + write_test!( + sha512, + "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\ + 47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", + "57ecf739d3a7ca647639adae80a05f4f361304bfcbfa1ceba93296b096e74287\ + 45fc10c142cecdd3bb587a3dba598c072f6f78b31cc0a06a3da0105ee51f75d6", + "dd28f78c53f3bc9bd0c2dca9642a1ad402a70412f985c1f6e54fadb98ce9c458\ + 4761df8d04ed04bb734ba48dd2106bb9ea54524f1394cdd18e6da3166e71c3ee", + ); + + write_test!( + sha512_256, + "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a", + "8d4bb96e7956cf5f08bf5c45f7982630c46b0b022f25cbaf722ae97c06a6e7a2", + "3367646f3e264653f7dd664ac2cb6d3b96329e86ffb7a29a1082e2a4ddc9ee7a", + ); + + write_test!( + ripemd160, + "9c1185a5c5e9fc54612808977ee8f548b2258d31", + "e571a1ca5b780aa52bafdb9ec852544ffca418ba", + "ddd2ecce739e823629c7d46ab18918e9c4a51c75", + ); + + write_test!( + hash160, + "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", + "671356a1a874695ad3bc20cae440f4360835bd5a", + "a9608c952c8dbcc20c53803d2ca5ad31d64d9313", + ); + + #[test] + fn hmac() { + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + engine.write_all(&[]).unwrap(); + assert_eq!( + format!("{}", hmac::Hmac::from_engine(engine)), + "bf5515149cf797955c4d3194cca42472883281951697c8375d9d9b107f384225" + ); + + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + engine.write_all(&[1; 256]).unwrap(); + assert_eq!( + format!("{}", hmac::Hmac::from_engine(engine)), + "59c9aca10c81c73cb4c196d94db741b6bf2050e0153d5a45f2526bff34675ac5" + ); + + let mut engine = hmac::HmacEngine::::new(&[0xde, 0xad, 0xbe, 0xef]); + engine.write_all(&[99; 64000]).unwrap(); + assert_eq!( + format!("{}", hmac::Hmac::from_engine(engine)), + "30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08" + ); + } + + #[test] + fn siphash24() { + let mut engine = siphash24::HashEngine::with_keys(0, 0); + engine.write_all(&[]).unwrap(); + assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "d70077739d4b921e"); + + let mut engine = siphash24::HashEngine::with_keys(0, 0); + engine.write_all(&[1; 256]).unwrap(); + assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "3a3ccefde9b5b1e3"); + + let mut engine = siphash24::HashEngine::with_keys(0, 0); + engine.write_all(&[99; 64000]).unwrap(); + assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "ce456e4e4ecbc5bf"); + } + + // Data and expected hashes taken from `bitcoin_hashes/tests/regression.rs`. + const DATA: &str = "arbitrary data to hash as a regression test"; + const HMAC_KEY: &[u8] = b"some key"; + + macro_rules! impl_hash_reader_test { + ($($test_name:ident, $module:ident, $want:literal);* $(;)?) => { + $( + #[test] + fn $test_name() { + let hash = $module::Hash::hash(DATA.as_bytes()); + let got = format!("{}", hash); + assert_eq!(got, $want); + + let mut reader = Cursor::new(DATA); + let hash_from_reader = $module::Hash::hash_reader(&mut reader).unwrap(); + assert_eq!(hash_from_reader, hash) + } + )* + } + } + + impl_hash_reader_test! { + hash_from_reader_hash160, hash160, "a17909f6d5373b0085c4180ba207126e5040f74d"; + hash_from_reader_ripemd160, ripemd160, "e6801701c77a1cd85662335258c7869631b4a9a8"; + hash_from_reader_sha1, sha1, "e1e81eeabadafa3d5d41cc3f405385426b0f47fd"; + hash_from_reader_sha256, sha256, "d291c6c5a07fa1d9315cdae090ebe14169fbe0a219cd55a48d0d2104eab6ec51"; + hash_from_reader_sha256d, sha256d, "93a743b022290bde3233a619b21aaebe06c5cf5cc959464c41be35711e37731b"; + hash_from_reader_sha384, sha384, "f545bd83d297978d47a7f26b858a54188499dfb4d7d570a6a2362c765031d57a29d7e002df5e34d184e70b65a4f47153"; + hash_from_reader_sha512, sha512, "057d0a37e9e0ac9a93acde0752748da059a27bcf946c7af00692ac1a95db8d21f965f40af22efc4710f100f8d3e43f79f77b1f48e1e400a95b7344b7bc0dfd10"; + hash_from_reader_sha512_256, sha512_256, "e204244c429b5bca037a2a8a6e7ed8a42b808ceaff182560840bb8c5c8e9a2ec"; + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] + pub struct RegHashTag; // Name comes from regression tests in `bitcoin_hashes`. + + impl sha256t::Tag for RegHashTag { + const MIDSTATE: sha256::Midstate = sha256::Midstate::new([0xab; 32], 64); + } + + type RegHash = sha256t::Hash; + + #[test] + fn regression_sha256t() { + let hash = RegHash::hash(DATA.as_bytes()); + let got = format!("{}", hash); + let want = "17db326d7c13867376ccca1f8a211377be3cbeaeb372f167822284866ddf14ca"; + assert_eq!(got, want); + } + + #[test] + fn regression_hmac_sha256_with_key() { + let mut engine = HmacEngine::::new(HMAC_KEY); + engine.input(DATA.as_bytes()); + let hash = Hmac::from_engine(engine); + + let got = format!("{}", hash); + let want = "d159cecaf4adf90b6a641bab767e4817d3a51c414acea3682686c35ec0b37b52"; + assert_eq!(got, want); + } + + #[test] + fn regression_hmac_sha512_with_key() { + let mut engine = HmacEngine::::new(HMAC_KEY); + engine.input(DATA.as_bytes()); + let hash = Hmac::from_engine(engine); + + let got = format!("{}", hash); + let want = "8511773748f89ba22c07fb3a2981a12c1823695119de41f4a62aead6b848bd34939acf16475c35ed7956114fead3e794cc162ecd35e447a4dabc3227d55f757b"; + assert_eq!(got, want); + } + + #[test] + fn regression_siphash24_with_key() { + let mut engine = siphash24::HashEngine::with_keys(0, 0); + engine.input(DATA.as_bytes()); + let hash = siphash24::Hash::from_engine(engine); + + let got = format!("{}", hash); + let want = "e823ed82311d601a"; + assert_eq!(got, want); + } +} diff --git a/io/src/lib.rs b/io/src/lib.rs index 0e1d33126..3c020d0b4 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -25,11 +25,17 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "hashes")] +pub extern crate hashes; + #[cfg(feature = "std")] mod bridge; mod error; mod macros; +#[cfg(feature = "hashes")] +mod hash; + #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::vec::Vec; use core::cmp; @@ -39,6 +45,8 @@ 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; /// Result type returned by functions in this crate. pub type Result = core::result::Result; diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a44c42c6e..241be623a 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -16,16 +16,15 @@ exclude = ["tests", "contrib"] [features] default = ["std"] -std = ["alloc", "hashes/std", "hex/std", "internals/std", "io/std", "units/std"] -alloc = ["hashes/alloc", "hex/alloc", "internals/alloc", "io/alloc", "units/alloc"] +std = ["alloc", "hashes/std", "hex/std", "internals/std", "units/std"] +alloc = ["hashes/alloc", "hex/alloc", "internals/alloc", "units/alloc"] serde = ["dep:serde", "hashes/serde", "internals/serde", "units/serde", "alloc"] arbitrary = ["dep:arbitrary", "units/arbitrary"] [dependencies] -hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, features = ["bitcoin-io", "hex"] } +hashes = { package = "bitcoin_hashes", version = "0.16.0", default-features = false, features = ["hex"] } hex = { package = "hex-conservative", version = "0.3.0", default-features = false } internals = { package = "bitcoin-internals", version = "0.4.0" } -io = { package = "bitcoin-io", version = "0.2.0", default-features = false } units = { package = "bitcoin-units", version = "0.2.0", default-features = false } arbitrary = { version = "1.4", optional = true }