Merge rust-bitcoin/rust-bitcoin#4085: Remove the `GeneralHash` trait

95ad91cdb6 hashes: Remove the GeneralHash trait (Tobin C. Harding)
6426e59c63 Remove unused trait import (Tobin C. Harding)
791501eabc io: Use function in place of GeneralHashExt (Tobin C. Harding)
2b6ef31469 hashes: Add hash_byte_chunks function to modules (Tobin C. Harding)
d3846895d7 hashes: Add hash function to modules (Tobin C. Harding)
e1bac7da55 Bound HmacEngine on HashEngine (Tobin C. Harding)
ab63b7a0ff Add Hash type and finalize method to HashEngine (Tobin C. Harding)
84623ffaf9 Add hash_again regression test (Tobin C. Harding)

Pull request description:

  This is the done as part of #4051.

  Requires some surgery on the `Hmac` and `Hkdf` types as well as a few other patches to maintain the logic that is currently provided by the trait. Final patch is a pure red diff - enjoy.

ACKs for top commit:
  Kixunil:
    ACK 95ad91cdb6
  apoelstra:
    ACK 95ad91cdb64c8870d3eb992090bab7a70d1369b9; successfully ran local tests

Tree-SHA512: bfd215de51c115144c6f5b3430b17dad5d770a8c876fe3775af2828ec620a1f4e4155e63bb18dac244a82c3840413a615d55a0fef91b6949d3de319aa5bb8c2f
This commit is contained in:
merge-script 2025-03-07 16:35:09 +00:00
commit 5581c49e0f
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
21 changed files with 242 additions and 224 deletions

View File

@ -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<InvalidBase58PayloadLengthError> for Error {
impl Xpriv {
/// Constructs a new master key from a seed value
pub fn new_master(network: impl Into<NetworkKind>, seed: &[u8]) -> Result<Xpriv, Error> {
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(b"Bitcoin seed");
hmac_engine.input(seed);
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
let mut engine = HmacEngine::<sha512::HashEngine>::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<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Xpriv {
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
let mut engine = HmacEngine::<sha512::HashEngine>::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<sha512::Hash> = Hmac::from_engine(hmac_engine);
engine.input(&u32::from(i).to_be_bytes());
let hmac: Hmac<sha512::Hash> = 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<sha512::Hash> =
HmacEngine::new(&self.chain_code[..]);
hmac_engine.input(&self.public_key.serialize()[..]);
hmac_engine.input(&n.to_be_bytes());
let mut engine = HmacEngine::<sha512::HashEngine>::new(&self.chain_code[..]);
engine.input(&self.public_key.serialize()[..]);
engine.input(&n.to_be_bytes());
let hmac_result: Hmac<sha512::Hash> = 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))
}
}

View File

@ -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 = <sha256d::Hash as GeneralHash>::hash(data);
let checksum = sha256d::hash(data);
let checksum = checksum.to_byte_array();
[checksum[0], checksum[1], checksum[2], checksum[3]]
}

View File

@ -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<u8> = 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()),

View File

@ -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 => {

View File

@ -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) {

View File

@ -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<T: GeneralHash> {
pub struct Hkdf<T: HashEngine> {
/// Pseudorandom key based on the extract step.
prk: Hmac<T>,
prk: Hmac<T::Hash>,
}
impl<T: GeneralHash> Hkdf<T>
impl<T: HashEngine> Hkdf<T>
where
<T as GeneralHash>::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<T> = HmacEngine::new(salt);
hmac_engine.input(ikm);
Self { prk: Hmac::from_engine(hmac_engine) }
let mut engine: HmacEngine<T> = 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<T> = HmacEngine::new(self.prk.as_ref());
let mut engine: HmacEngine<T> = 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<T: GeneralHash> fmt::Debug for Hkdf<T> {
impl<T: HashEngine> fmt::Debug for Hkdf<T> {
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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::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::<sha256::Hash>::new(&salt, &ikm);
let hkdf = Hkdf::<sha256::HashEngine>::new(&salt, &ikm);
let debug = alloc::format!("{:?}", hkdf);
assert_eq!(debug, "Hkdf(#ec7bd36ab2ed4045)");

View File

@ -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: GeneralHash>(T);
pub struct Hmac<T: Hash>(T);
impl<T: GeneralHash> Hmac<T> {
/// Constructs a new keyed HMAC engine from `key`.
pub fn engine(key: &[u8]) -> HmacEngine<T>
where
<T as GeneralHash>::Engine: Default,
{
HmacEngine::new(key)
}
}
impl<T: GeneralHash + str::FromStr> str::FromStr for Hmac<T> {
impl<T: Hash + str::FromStr> str::FromStr for Hmac<T> {
type Err = <T as str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> { 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<T: GeneralHash> {
iengine: T::Engine,
oengine: T::Engine,
pub struct HmacEngine<T: HashEngine> {
iengine: T,
oengine: T,
}
impl<T: GeneralHash> HmacEngine<T> {
impl<T: HashEngine> HmacEngine<T> {
/// Constructs a new keyed HMAC engine from `key`.
///
/// We only support underlying hashes whose block sizes are ≤ 128 bytes.
@ -51,24 +41,23 @@ impl<T: GeneralHash> HmacEngine<T> {
/// Larger hashes will result in a panic.
pub fn new(key: &[u8]) -> HmacEngine<T>
where
<T as GeneralHash>::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: <T as GeneralHash>::engine(),
oengine: <T as GeneralHash>::engine(),
};
let mut ret = HmacEngine { iengine: T::default(), oengine: T::default() };
if key.len() > T::Engine::BLOCK_SIZE {
let hash = <T as GeneralHash>::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<T: GeneralHash> HmacEngine<T> {
}
};
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<T> {
pub fn from_inner_engines(iengine: T, oengine: T) -> HmacEngine<T> {
HmacEngine { iengine, oengine }
}
}
impl<T: GeneralHash> HashEngine for HmacEngine<T> {
const BLOCK_SIZE: usize = T::Engine::BLOCK_SIZE;
impl<T: HashEngine> HashEngine for HmacEngine<T> {
type Hash = Hmac<T::Hash>;
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<T: GeneralHash + fmt::Debug> fmt::Debug for Hmac<T> {
impl<T: Hash + fmt::Debug> fmt::Debug for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
}
impl<T: GeneralHash + fmt::Display> fmt::Display for Hmac<T> {
impl<T: Hash + fmt::Display> fmt::Display for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
impl<T: GeneralHash + fmt::LowerHex> fmt::LowerHex for Hmac<T> {
impl<T: Hash + fmt::LowerHex> fmt::LowerHex for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl<T: GeneralHash> convert::AsRef<[u8]> for Hmac<T> {
impl<T: Hash> convert::AsRef<[u8]> for Hmac<T> {
// Calling as_byte_array is more reliable
fn as_ref(&self) -> &[u8] { self.0.as_byte_array().as_ref() }
}
impl<T: GeneralHash> GeneralHash for Hmac<T> {
type Engine = HmacEngine<T>;
fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> {
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<T: GeneralHash> Hash for Hmac<T> {
impl<T: Hash> Hash for Hmac<T> {
type Bytes = T::Bytes;
fn from_byte_array(bytes: T::Bytes) -> Self { Hmac(T::from_byte_array(bytes)) }
@ -143,14 +127,14 @@ impl<T: GeneralHash> Hash for Hmac<T> {
}
#[cfg(feature = "serde")]
impl<T: GeneralHash + Serialize> Serialize for Hmac<T> {
impl<T: Hash + Serialize> Serialize for Hmac<T> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
Serialize::serialize(&self.0, s)
}
}
#[cfg(feature = "serde")]
impl<'de, T: GeneralHash + Deserialize<'de>> Deserialize<'de> for Hmac<T> {
impl<'de, T: Hash + Deserialize<'de>> Deserialize<'de> for Hmac<T> {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Hmac<T>, 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::<sha256::Hash>::new(test.key);
let mut engine = HmacEngine::<sha256::HashEngine>::new(test.key);
engine.input(test.input);
let hash = Hmac::<sha256::Hash>::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::<sha256::Hash>::engine(&[]);
let mut engine = HmacEngine::<sha256::HashEngine>::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::<sha256::Hash>::engine(&[]);
let mut engine = HmacEngine::<sha256::HashEngine>::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::<sha256::Hash>::engine(&[]);
let mut engine = HmacEngine::<sha256::HashEngine>::new(&[]);
let bytes = [1u8; 65536];
bh.iter(|| {
engine.input(&bytes);

View File

@ -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<B, I>(byte_slices: I) -> Hash
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
{
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 { <Self as $crate::GeneralHash>::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<B, I>(byte_slices: I) -> Self
@ -90,7 +108,7 @@ macro_rules! general_hash_type {
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
{
<Self as $crate::GeneralHash>::hash_byte_chunks(byte_slices)
hash_byte_chunks(byte_slices)
}
}
};

View File

@ -186,6 +186,15 @@ pub type HkdfSha512 = Hkdf<sha512::Hash>;
/// 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<B, I>(byte_slices: I) -> Self
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
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.

View File

@ -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) }
}

View File

@ -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) }
}

View File

@ -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(<Self as crate::GeneralHash>::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.
///

View File

@ -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)]

View File

@ -9,6 +9,34 @@ use core::marker::PhantomData;
use crate::sha256::Midstate;
use crate::{sha256, HashEngine as _};
/// Hashes some bytes.
pub fn hash<T>(data: &[u8]) -> Hash<T>
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<B, I, T>(byte_slices: I) -> Hash<T>
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
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<T: Tag> Clone for HashEngine<T> {
}
impl<T: Tag> crate::HashEngine for HashEngine<T> {
type Hash = Hash<T>;
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!(

View File

@ -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)]

View File

@ -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) }
}

View File

@ -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)]

View File

@ -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 {

View File

@ -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::<sha256::Hash>::new(HMAC_KEY);
let mut engine = HmacEngine::<sha256::HashEngine>::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::<sha512::Hash>::new(HMAC_KEY);
let mut engine = HmacEngine::<sha512::HashEngine>::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);
}

View File

@ -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,20 +130,15 @@ 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<R: crate::BufRead>(reader: &mut R) -> Result<Self, crate::Error>
where
Self::Engine: Default,
{
let mut engine = Self::engine(); // This calls `Self::Engine::default()`.
pub fn hash_reader<T>(reader: &mut impl BufRead) -> Result<T::Hash, crate::Error>
where
T: hashes::HashEngine + Default,
{
let mut engine = T::default();
loop {
let bytes = reader.fill_buf()?;
@ -154,17 +151,7 @@ pub trait GeneralHashExt: GeneralHash + sealed::Sealed {
engine.input(bytes);
reader.consume(read);
}
Ok(Self::from_engine(engine))
}
}
impl<T: GeneralHash> GeneralHashExt for T {}
mod sealed {
/// Used to seal the `GeneralHashExt` trait.
pub trait Sealed {}
impl<T: hashes::GeneralHash> 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::<sha256::Hash>::new(&[0xde, 0xad, 0xbe, 0xef]);
let mut engine = hmac::HmacEngine::<sha256::HashEngine>::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::<sha256::Hash>::new(&[0xde, 0xad, 0xbe, 0xef]);
let mut engine = hmac::HmacEngine::<sha256::HashEngine>::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::<sha256::Hash>::new(&[0xde, 0xad, 0xbe, 0xef]);
let mut engine = hmac::HmacEngine::<sha256::HashEngine>::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::<sha256::Hash>::new(HMAC_KEY);
let mut engine = HmacEngine::<sha256::HashEngine>::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::<sha512::Hash>::new(HMAC_KEY);
let mut engine = HmacEngine::<sha512::HashEngine>::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";

View File

@ -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<T> = core::result::Result<T, Error>;