Skip to content

Commit 8c0de95

Browse files
committed
Merge rust-bitcoin#3113: Push up the Default bound on HashEngine in order to better support keyed hash functions
2969b03 Push up the Default bound on HashEngine (Nick Johnson) Pull request description: Backstory for this patch is in rust-bitcoin#3084, and this might be enough to close that issue, but I think some follow up work might be required. I took Kixunil's advice and pushed up the `Default` bound on `HashEngine` all the way to the default methods themselves on the `GeneralHash` trait. I initially placed the bound just on the assocated type `Engine` of `GeneralHash`, which is a tad cleaner and keeps the bound from "leaking" downstream into things like `Hmac` and `Hkdf` implementations. However, that restricts `GeneralHash` implementations to just unkeyed hash functions and I believe there will be value in having `Poly1305` and `SipHash24` still leverage the interface. I struggled a bit on the best spots to put these bounds (even found the syntax a little jarring at times, I found [RFC2289](https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html) helpful if others do too). Refactored the existing keyed hash function, `SipHash24`, to no longer have `Default` functions with zero-value keys. I kept the test coverage though by just hardcoding the zero-value keys over in the tests. Refactoring the `hash_type` macro for keyed hashes was getting a little hairy, so backed off and just wrote the bare minimum for `SipHash24` inline. Once `Poly1305` lands there will be two keyed hash functions and I think it will make more sense to then generalize over them. ACKs for top commit: Kixunil: ACK 2969b03 apoelstra: ACK 2969b03 Tree-SHA512: 9ca8f8baa6d60c36627eb3564f5faafcddd0a69fed5e29965404e405d3d09cb08bec2e05f14e177b8bfa2a488efba9947c0e0c158db6b00b44c8c11869328f5a
2 parents 877ed23 + 2969b03 commit 8c0de95

File tree

6 files changed

+94
-24
lines changed

6 files changed

+94
-24
lines changed

hashes/src/hkdf.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ pub struct Hkdf<T: GeneralHash> {
3737
prk: Hmac<T>,
3838
}
3939

40-
impl<T: GeneralHash> Hkdf<T> {
40+
impl<T: GeneralHash> Hkdf<T>
41+
where
42+
<T as GeneralHash>::Engine: Default,
43+
{
4144
/// Initialize a HKDF by performing the extract step.
4245
pub fn new(salt: &[u8], ikm: &[u8]) -> Self {
4346
let mut hmac_engine: HmacEngine<T> = HmacEngine::new(salt);

hashes/src/hmac.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ pub struct HmacEngine<T: GeneralHash> {
4242
oengine: T::Engine,
4343
}
4444

45-
impl<T: GeneralHash> Default for HmacEngine<T> {
45+
impl<T: GeneralHash> Default for HmacEngine<T>
46+
where
47+
<T as GeneralHash>::Engine: Default,
48+
{
4649
fn default() -> Self { HmacEngine::new(&[]) }
4750
}
4851

@@ -54,7 +57,10 @@ impl<T: GeneralHash> HmacEngine<T> {
5457
/// # Panics
5558
///
5659
/// Larger hashes will result in a panic.
57-
pub fn new(key: &[u8]) -> HmacEngine<T> {
60+
pub fn new(key: &[u8]) -> HmacEngine<T>
61+
where
62+
<T as GeneralHash>::Engine: Default,
63+
{
5864
debug_assert!(T::Engine::BLOCK_SIZE <= 128);
5965

6066
let mut ipad = [0x36u8; 128];

hashes/src/impls.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ mod tests {
153153
"a9608c952c8dbcc20c53803d2ca5ad31d64d9313",
154154
);
155155

156-
write_test!(siphash24, "d70077739d4b921e", "3a3ccefde9b5b1e3", "ce456e4e4ecbc5bf",);
157-
158156
#[test]
159157
fn hmac() {
160158
let mut engine = hmac::HmacEngine::<sha256::Hash>::new(&[0xde, 0xad, 0xbe, 0xef]);
@@ -178,4 +176,19 @@ mod tests {
178176
"30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08"
179177
);
180178
}
179+
180+
#[test]
181+
fn siphash24() {
182+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
183+
engine.write_all(&[]).unwrap();
184+
assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "d70077739d4b921e");
185+
186+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
187+
engine.write_all(&[1; 256]).unwrap();
188+
assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "3a3ccefde9b5b1e3");
189+
190+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
191+
engine.write_all(&[99; 64000]).unwrap();
192+
assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "ce456e4e4ecbc5bf");
193+
}
181194
}

hashes/src/lib.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ pub type HkdfSha256 = Hkdf<sha256::Hash>;
177177
pub type HkdfSha512 = Hkdf<sha512::Hash>;
178178

179179
/// A hashing engine which bytes can be serialized into.
180-
pub trait HashEngine: Clone + Default {
180+
pub trait HashEngine: Clone {
181181
/// Length of the hash's internal block size, in bytes.
182182
const BLOCK_SIZE: usize;
183183

@@ -189,20 +189,31 @@ pub trait HashEngine: Clone + Default {
189189
}
190190

191191
/// Trait describing hash digests which can be constructed by hashing arbitrary data.
192+
///
193+
/// Some methods have been bound to engines which implement Default, which is
194+
/// generally an unkeyed hash function.
192195
pub trait GeneralHash: Hash {
193196
/// A hashing engine which bytes can be serialized into. It is expected
194197
/// to implement the `io::Write` trait, and to never return errors under
195198
/// any conditions.
196199
type Engine: HashEngine;
197200

198201
/// Constructs a new engine.
199-
fn engine() -> Self::Engine { Self::Engine::default() }
202+
fn engine() -> Self::Engine
203+
where
204+
Self::Engine: Default,
205+
{
206+
Self::Engine::default()
207+
}
200208

201209
/// Produces a hash from the current state of a given engine.
202210
fn from_engine(e: Self::Engine) -> Self;
203211

204212
/// Hashes some bytes.
205-
fn hash(data: &[u8]) -> Self {
213+
fn hash(data: &[u8]) -> Self
214+
where
215+
Self::Engine: Default,
216+
{
206217
let mut engine = Self::engine();
207218
engine.input(data);
208219
Self::from_engine(engine)
@@ -213,6 +224,7 @@ pub trait GeneralHash: Hash {
213224
where
214225
B: AsRef<[u8]>,
215226
I: IntoIterator<Item = B>,
227+
Self::Engine: Default,
216228
{
217229
let mut engine = Self::engine();
218230
for slice in byte_slices {

hashes/src/siphash24.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ use core::ops::Index;
66
use core::slice::SliceIndex;
77
use core::{cmp, mem, ptr};
88

9+
use crate::internal_macros::arr_newtype_fmt_impl;
910
use crate::HashEngine as _;
1011

11-
crate::internal_macros::hash_type! {
12-
64,
13-
false,
14-
"Output of the SipHash24 hash function."
15-
}
12+
#[doc = "Output of the SipHash24 hash function."]
13+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14+
#[repr(transparent)]
15+
pub struct Hash([u8; 8]);
1616

1717
#[cfg(not(hashes_fuzz))]
1818
fn from_engine(e: HashEngine) -> Hash { Hash::from_u64(Hash::from_engine_to_u64(e)) }
@@ -106,10 +106,6 @@ impl HashEngine {
106106
}
107107
}
108108

109-
/// Creates a new SipHash24 engine.
110-
#[inline]
111-
pub const fn new() -> HashEngine { HashEngine::with_keys(0, 0) }
112-
113109
/// Retrieves the keys of this engine.
114110
pub fn keys(&self) -> (u64, u64) { (self.k0, self.k1) }
115111

@@ -128,10 +124,6 @@ impl HashEngine {
128124
}
129125
}
130126

131-
impl Default for HashEngine {
132-
fn default() -> Self { HashEngine::new() }
133-
}
134-
135127
impl crate::HashEngine for HashEngine {
136128
const BLOCK_SIZE: usize = 8;
137129

@@ -186,6 +178,9 @@ impl Hash {
186178
Hash::from_engine(engine)
187179
}
188180

181+
/// Produces a hash from the current state of a given engine.
182+
pub fn from_engine(e: HashEngine) -> Hash { from_engine(e) }
183+
189184
/// Hashes the given data directly to u64 with an engine with the provided keys.
190185
pub fn hash_to_u64_with_keys(k0: u64, k1: u64, data: &[u8]) -> u64 {
191186
let mut engine = HashEngine::with_keys(k0, k1);
@@ -215,8 +210,39 @@ impl Hash {
215210

216211
/// Creates a hash from its (little endian) 64-bit integer representation.
217212
pub fn from_u64(hash: u64) -> Hash { Hash(hash.to_le_bytes()) }
213+
214+
fn from_slice(sl: &[u8]) -> Result<Self, crate::FromSliceError> {
215+
let mut ret = [0; 8];
216+
ret.copy_from_slice(sl);
217+
Ok(Hash(ret))
218+
}
219+
}
220+
221+
impl crate::Hash for Hash {
222+
type Bytes = [u8; 8];
223+
224+
const LEN: usize = 8;
225+
const DISPLAY_BACKWARD: bool = false;
226+
227+
fn from_slice(sl: &[u8]) -> Result<Self, crate::FromSliceError> { Self::from_slice(sl) }
228+
229+
fn to_byte_array(self) -> Self::Bytes { self.0 }
230+
231+
fn as_byte_array(&self) -> &Self::Bytes { &self.0 }
232+
233+
fn from_byte_array(bytes: Self::Bytes) -> Self { Hash(bytes) }
218234
}
219235

236+
impl<I: SliceIndex<[u8]>> Index<I> for Hash {
237+
type Output = I::Output;
238+
239+
#[inline]
240+
fn index(&self, index: I) -> &Self::Output { &self.0[index] }
241+
}
242+
243+
arr_newtype_fmt_impl!(Hash, 8);
244+
borrow_slice_impl!(Hash);
245+
220246
/// Load an u64 using up to 7 bytes of a byte slice.
221247
///
222248
/// Unsafe because: unchecked indexing at `start..start+len`.
@@ -341,7 +367,7 @@ mod benches {
341367

342368
#[bench]
343369
pub fn siphash24_1ki(bh: &mut Bencher) {
344-
let mut engine = siphash24::Hash::engine();
370+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
345371
let bytes = [1u8; 1024];
346372
bh.iter(|| {
347373
engine.input(&bytes);
@@ -351,7 +377,7 @@ mod benches {
351377

352378
#[bench]
353379
pub fn siphash24_64ki(bh: &mut Bencher) {
354-
let mut engine = siphash24::Hash::engine();
380+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
355381
let bytes = [1u8; 65536];
356382
bh.iter(|| {
357383
engine.input(&bytes);

hashes/tests/regression.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ impl_regression_test! {
3030
regression_sha384, sha384, "f545bd83d297978d47a7f26b858a54188499dfb4d7d570a6a2362c765031d57a29d7e002df5e34d184e70b65a4f47153";
3131
regression_sha512, sha512, "057d0a37e9e0ac9a93acde0752748da059a27bcf946c7af00692ac1a95db8d21f965f40af22efc4710f100f8d3e43f79f77b1f48e1e400a95b7344b7bc0dfd10";
3232
regression_sha512_256, sha512_256, "e204244c429b5bca037a2a8a6e7ed8a42b808ceaff182560840bb8c5c8e9a2ec";
33-
regression_siphash24, siphash24, "e823ed82311d601a";
3433
}
3534

3635
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
@@ -90,3 +89,14 @@ fn regression_hmac_sha512_with_key() {
9089
let want = "8511773748f89ba22c07fb3a2981a12c1823695119de41f4a62aead6b848bd34939acf16475c35ed7956114fead3e794cc162ecd35e447a4dabc3227d55f757b";
9190
assert_eq!(got, want);
9291
}
92+
93+
#[test]
94+
fn regression_siphash24_with_key() {
95+
let mut engine = siphash24::HashEngine::with_keys(0, 0);
96+
engine.input(DATA.as_bytes());
97+
let hash = siphash24::Hash::from_engine(engine);
98+
99+
let got = format!("{}", hash);
100+
let want = "e823ed82311d601a";
101+
assert_eq!(got, want);
102+
}

0 commit comments

Comments
 (0)