hashes: split Hash trait into two

This commit is contained in:
Andrew Poelstra 2024-06-16 13:40:51 +00:00
parent 1fe4c63986
commit 73dcc79763
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
12 changed files with 97 additions and 74 deletions

View File

@ -9,7 +9,7 @@ use core::ops::Index;
use core::str::FromStr;
use core::{fmt, slice};
use hashes::{hash160, hash_newtype, sha512, Hash, HashEngine, Hmac, HmacEngine};
use hashes::{hash160, hash_newtype, sha512, GeneralHash, HashEngine, Hmac, HmacEngine};
use internals::{impl_array_newtype, write_err};
use io::Write;
use secp256k1::{Secp256k1, XOnlyPublicKey};

View File

@ -16,7 +16,7 @@
use core::{fmt, mem};
use hashes::{sha256, sha256d, Hash};
use hashes::{sha256, sha256d, GeneralHash, Hash};
use hex::error::{InvalidCharError, OddLengthStringError};
use internals::write_err;
use io::{BufRead, Cursor, Read, Write};
@ -768,7 +768,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 Hash>::hash(data);
let checksum = <sha256d::Hash as GeneralHash>::hash(data);
[checksum[0], checksum[1], checksum[2], checksum[3]]
}

View File

@ -121,7 +121,7 @@ macro_rules! psbt_insert_hash_pair {
match $slf.$map.entry(key_val) {
btree_map::Entry::Vacant(empty_key) => {
let val: Vec<u8> = Deserialize::deserialize(&$raw_value)?;
if <$hash as hashes::Hash>::hash(&val) != key_val {
if <$hash as hashes::GeneralHash>::hash(&val) != key_val {
return Err(psbt::Error::InvalidPreimageHashPair {
preimage: val.into_boxed_slice(),
hash: Box::from(key_val.borrow()),

View File

@ -34,7 +34,8 @@ mod tests {
#[test]
#[cfg(feature = "alloc")]
fn test() {
use crate::{hash160, Hash, HashEngine};
use super::Hash;
use crate::{hash160, HashEngine};
#[derive(Clone)]
#[cfg(feature = "alloc")]
@ -111,7 +112,7 @@ mod tests {
mod benches {
use test::Bencher;
use crate::{hash160, Hash, HashEngine};
use crate::{hash160, GeneralHash as _, 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::{Hash, HashEngine, Hmac, HmacEngine};
use crate::{GeneralHash, HashEngine, Hmac, HmacEngine};
/// Output keying material max length multiple.
const MAX_OUTPUT_BLOCKS: usize = 255;
@ -32,12 +32,12 @@ impl fmt::Display for MaxLengthError {
impl std::error::Error for MaxLengthError {}
/// HMAC-based Extract-and-Expand Key Derivation Function (HKDF).
pub struct Hkdf<T: Hash> {
pub struct Hkdf<T: GeneralHash> {
/// Pseudorandom key based on the extract step.
prk: Hmac<T>,
}
impl<T: Hash> Hkdf<T> {
impl<T: GeneralHash> Hkdf<T> {
/// 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);

View File

@ -12,15 +12,15 @@ use core::{convert, fmt, str};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{FromSliceError, Hash, HashEngine};
use crate::{FromSliceError, GeneralHash, 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: Hash>(T);
pub struct Hmac<T: GeneralHash>(T);
#[cfg(feature = "schemars")]
impl<T: Hash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> {
impl<T: GeneralHash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> {
fn is_referenceable() -> bool { <T as schemars::JsonSchema>::is_referenceable() }
fn schema_name() -> std::string::String { <T as schemars::JsonSchema>::schema_name() }
@ -30,13 +30,13 @@ impl<T: Hash + schemars::JsonSchema> schemars::JsonSchema for Hmac<T> {
}
}
impl<T: Hash + str::FromStr> str::FromStr for Hmac<T> {
impl<T: GeneralHash + 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 midstates which represent the current state of an `HmacEngine`.
pub struct HmacMidState<T: Hash> {
pub struct HmacMidState<T: GeneralHash> {
/// Midstate of the inner hash engine
pub inner: <T::Engine as HashEngine>::MidState,
/// Midstate of the outer hash engine
@ -45,16 +45,16 @@ pub struct HmacMidState<T: Hash> {
/// Pair of underlying hash engines, used for the inner and outer hash of HMAC.
#[derive(Clone)]
pub struct HmacEngine<T: Hash> {
pub struct HmacEngine<T: GeneralHash> {
iengine: T::Engine,
oengine: T::Engine,
}
impl<T: Hash> Default for HmacEngine<T> {
impl<T: GeneralHash> Default for HmacEngine<T> {
fn default() -> Self { HmacEngine::new(&[]) }
}
impl<T: Hash> HmacEngine<T> {
impl<T: GeneralHash> HmacEngine<T> {
/// Constructs a new keyed HMAC from `key`.
///
/// We only support underlying hashes whose block sizes are ≤ 128 bytes.
@ -67,10 +67,13 @@ impl<T: Hash> HmacEngine<T> {
let mut ipad = [0x36u8; 128];
let mut opad = [0x5cu8; 128];
let mut ret = HmacEngine { iengine: <T as Hash>::engine(), oengine: <T as Hash>::engine() };
let mut ret = HmacEngine {
iengine: <T as GeneralHash>::engine(),
oengine: <T as GeneralHash>::engine(),
};
if key.len() > T::Engine::BLOCK_SIZE {
let hash = <T as Hash>::hash(key);
let hash = <T as GeneralHash>::hash(key);
for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) {
*b_i ^= *b_h;
}
@ -97,7 +100,7 @@ impl<T: Hash> HmacEngine<T> {
}
}
impl<T: Hash> HashEngine for HmacEngine<T> {
impl<T: GeneralHash> HashEngine for HmacEngine<T> {
type MidState = HmacMidState<T>;
fn midstate(&self) -> Self::MidState {
@ -111,25 +114,24 @@ impl<T: Hash> HashEngine for HmacEngine<T> {
fn input(&mut self, buf: &[u8]) { self.iengine.input(buf) }
}
impl<T: Hash> fmt::Debug for Hmac<T> {
impl<T: GeneralHash> fmt::Debug for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) }
}
impl<T: Hash> fmt::Display for Hmac<T> {
impl<T: GeneralHash> fmt::Display for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
impl<T: Hash> fmt::LowerHex for Hmac<T> {
impl<T: GeneralHash> fmt::LowerHex for Hmac<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
impl<T: Hash> convert::AsRef<[u8]> for Hmac<T> {
impl<T: GeneralHash> convert::AsRef<[u8]> for Hmac<T> {
fn as_ref(&self) -> &[u8] { self.0.as_ref() }
}
impl<T: Hash> Hash for Hmac<T> {
impl<T: GeneralHash> GeneralHash for Hmac<T> {
type Engine = HmacEngine<T>;
type Bytes = T::Bytes;
fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> {
let ihash = T::from_engine(e.iengine);
@ -137,7 +139,10 @@ impl<T: Hash> Hash for Hmac<T> {
let ohash = T::from_engine(e.oengine);
Hmac(ohash)
}
}
impl<T: GeneralHash> Hash for Hmac<T> {
type Bytes = T::Bytes;
const LEN: usize = T::LEN;
fn from_slice(sl: &[u8]) -> Result<Hmac<T>, FromSliceError> { T::from_slice(sl).map(Hmac) }
@ -150,14 +155,14 @@ impl<T: Hash> Hash for Hmac<T> {
}
#[cfg(feature = "serde")]
impl<T: Hash + Serialize> Serialize for Hmac<T> {
impl<T: GeneralHash + 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: Hash + Deserialize<'de>> Deserialize<'de> for Hmac<T> {
impl<'de, T: GeneralHash + 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))
@ -169,7 +174,7 @@ mod tests {
#[test]
#[cfg(feature = "alloc")]
fn test() {
use crate::{sha256, Hash, HashEngine, Hmac, HmacEngine};
use crate::{sha256, GeneralHash as _, Hash as _, HashEngine, Hmac, HmacEngine};
#[derive(Clone)]
struct Test {
@ -297,7 +302,7 @@ mod tests {
fn hmac_sha512_serde() {
use serde_test::{assert_tokens, Configure, Token};
use crate::{sha512, Hash, Hmac};
use crate::{sha512, Hash as _, Hmac};
#[rustfmt::skip]
static HASH_BYTES: [u8; 64] = [
@ -327,7 +332,7 @@ mod tests {
mod benches {
use test::Bencher;
use crate::{sha256, Hash, HashEngine, Hmac};
use crate::{sha256, GeneralHash as _, HashEngine, Hmac};
#[bench]
pub fn hmac_sha256_10(bh: &mut Bencher) {

View File

@ -60,14 +60,16 @@ impl_write!(
Ok(buf.len())
},
|_us| { Ok(()) },
T: crate::Hash
T: crate::GeneralHash
);
#[cfg(test)]
mod tests {
use bitcoin_io::Write;
use crate::{hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, Hash};
use crate::{
hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, GeneralHash as _,
};
macro_rules! write_test {
($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => {

View File

@ -99,16 +99,19 @@ macro_rules! hash_trait_impls {
}
}
impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> {
impl<$($gen: $gent),*> crate::GeneralHash for Hash<$($gen),*> {
type Engine = HashEngine;
type Bytes = [u8; $bits / 8];
const LEN: usize = $bits / 8;
const DISPLAY_BACKWARD: bool = $reverse;
fn engine() -> HashEngine { Self::engine() }
fn from_engine(e: HashEngine) -> Hash<$($gen),*> { Self::from_engine(e) }
}
impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> {
type Bytes = [u8; $bits / 8];
const LEN: usize = $bits / 8;
const DISPLAY_BACKWARD: bool = $reverse;
fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<Hash<$($gen),*>, FromSliceError> {
Self::from_slice(sl)

View File

@ -12,7 +12,7 @@
//! Hashing a single byte slice or a string:
//!
//! ```rust
//! use bitcoin_hashes::{sha256, Hash as _};
//! use bitcoin_hashes::{sha256, GeneralHash as _};
//!
//! let bytes = [0u8; 5];
//! let hash_of_bytes = sha256::Hash::hash(&bytes);
@ -23,7 +23,7 @@
//! Hashing content from a reader:
//!
//! ```rust
//! use bitcoin_hashes::{sha256, Hash as _};
//! use bitcoin_hashes::{sha256, GeneralHash as _};
//!
//! #[cfg(std)]
//! # fn main() -> std::io::Result<()> {
@ -147,40 +147,19 @@ pub trait HashEngine: Clone + Default {
fn n_bytes_hashed(&self) -> usize;
}
/// Trait which applies to hashes of all types.
pub trait Hash:
Copy
+ Clone
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ hash::Hash
+ fmt::Debug
+ fmt::Display
+ fmt::LowerHex
+ convert::AsRef<[u8]>
{
/// Trait describing hash digests which can be constructed by hashing arbitrary data.
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;
/// The byte array that represents the hash internally.
type Bytes: hex::FromHex + Copy;
/// Constructs a new engine.
fn engine() -> Self::Engine { Self::Engine::default() }
/// Produces a hash from the current state of a given engine.
fn from_engine(e: Self::Engine) -> Self;
/// Length of the hash, in bytes.
const LEN: usize;
/// Copies a byte slice into a hash object.
fn from_slice(sl: &[u8]) -> Result<Self, FromSliceError>;
/// Hashes some bytes.
fn hash(data: &[u8]) -> Self {
let mut engine = Self::engine();
@ -200,6 +179,30 @@ pub trait Hash:
}
Self::from_engine(engine)
}
}
/// Trait which applies to hashes of all types.
pub trait Hash:
Copy
+ Clone
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ hash::Hash
+ fmt::Debug
+ fmt::Display
+ fmt::LowerHex
+ convert::AsRef<[u8]>
{
/// The byte array that represents the hash internally.
type Bytes: hex::FromHex + Copy;
/// Length of the hash, in bytes.
const LEN: usize;
/// Copies a byte slice into a hash object.
fn from_slice(sl: &[u8]) -> Result<Self, FromSliceError>;
/// Flag indicating whether user-visible serializations of this hash
/// should be backward. For some reason Satoshi decided this should be

View File

@ -101,7 +101,7 @@ impl crate::HashEngine for HashEngine {
impl Hash {
/// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash
pub fn hash_again(&self) -> sha256d::Hash {
crate::Hash::from_byte_array(<Self as crate::Hash>::hash(&self.0).0)
crate::Hash::from_byte_array(<Self as crate::GeneralHash>::hash(&self.0).0)
}
/// Computes hash from `bytes` in `const` context.

View File

@ -210,13 +210,13 @@ macro_rules! hash_newtype {
}
/// Constructs a new engine.
pub fn engine() -> <$hash as $crate::Hash>::Engine {
<$hash as $crate::Hash>::engine()
pub fn engine() -> <$hash as $crate::GeneralHash>::Engine {
<$hash as $crate::GeneralHash>::engine()
}
/// Produces a hash from the current state of a given engine.
pub fn from_engine(e: <$hash as $crate::Hash>::Engine) -> Self {
Self::from(<$hash as $crate::Hash>::from_engine(e))
pub fn from_engine(e: <$hash as $crate::GeneralHash>::Engine) -> Self {
Self::from(<$hash as $crate::GeneralHash>::from_engine(e))
}
/// Copies a byte slice into a hash object.
@ -279,16 +279,12 @@ macro_rules! hash_newtype {
}
impl $crate::Hash for $newtype {
type Engine = <$hash as $crate::Hash>::Engine;
type Bytes = <$hash as $crate::Hash>::Bytes;
const LEN: usize = <$hash as $crate::Hash>::LEN;
const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*);
fn engine() -> <$hash as $crate::Hash>::Engine { Self::engine() }
fn from_engine(e: <$hash as $crate::Hash>::Engine) -> $newtype { Self::from_engine(e) }
#[inline]
fn from_slice(sl: &[u8]) -> $crate::_export::_core::result::Result<$newtype, $crate::FromSliceError> {
Self::from_slice(sl)
}
@ -300,6 +296,19 @@ macro_rules! hash_newtype {
fn from_byte_array(bytes: Self::Bytes) -> Self { Self::from_byte_array(bytes) }
}
// To be dropped in the next commit
impl $crate::GeneralHash for $newtype {
type Engine = <$hash as $crate::GeneralHash>::Engine;
fn engine() -> Self::Engine {
<$hash as $crate::GeneralHash>::engine()
}
fn from_engine(e: <$hash as $crate::GeneralHash>::Engine) -> $newtype {
Self::from_engine(e)
}
}
impl $crate::_export::_core::str::FromStr for $newtype {
type Err = $crate::hex::HexToArrayError;
fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> {

View File

@ -2,7 +2,7 @@
use bitcoin_hashes::{
hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24,
Hash as _, HashEngine as _, Hmac, HmacEngine,
GeneralHash as _, HashEngine as _, Hmac, HmacEngine,
};
const DATA: &str = "arbitrary data to hash as a regression test";