Push up the Default bound on HashEngine

* The Default bound only makes sense for unkeyed hash functions which
can fire up a new engine without a key. Keyed hash functions, like
SipHash24 or Poly1305 require a secret key to be initialized and
should not implement a default engine generator.
* SipHash24 tests updated to the previous default key "0".
This commit is contained in:
Nick Johnson 2024-07-30 11:30:18 -07:00
parent 80671bbea8
commit 2969b032f9
6 changed files with 94 additions and 24 deletions

View File

@ -37,7 +37,10 @@ pub struct Hkdf<T: GeneralHash> {
prk: Hmac<T>,
}
impl<T: GeneralHash> Hkdf<T> {
impl<T: GeneralHash> Hkdf<T>
where
<T as GeneralHash>::Engine: 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);

View File

@ -42,7 +42,10 @@ pub struct HmacEngine<T: GeneralHash> {
oengine: T::Engine,
}
impl<T: GeneralHash> Default for HmacEngine<T> {
impl<T: GeneralHash> Default for HmacEngine<T>
where
<T as GeneralHash>::Engine: Default,
{
fn default() -> Self { HmacEngine::new(&[]) }
}
@ -54,7 +57,10 @@ impl<T: GeneralHash> HmacEngine<T> {
/// # Panics
///
/// Larger hashes will result in a panic.
pub fn new(key: &[u8]) -> HmacEngine<T> {
pub fn new(key: &[u8]) -> HmacEngine<T>
where
<T as GeneralHash>::Engine: Default,
{
debug_assert!(T::Engine::BLOCK_SIZE <= 128);
let mut ipad = [0x36u8; 128];

View File

@ -153,8 +153,6 @@ mod tests {
"a9608c952c8dbcc20c53803d2ca5ad31d64d9313",
);
write_test!(siphash24, "d70077739d4b921e", "3a3ccefde9b5b1e3", "ce456e4e4ecbc5bf",);
#[test]
fn hmac() {
let mut engine = hmac::HmacEngine::<sha256::Hash>::new(&[0xde, 0xad, 0xbe, 0xef]);
@ -178,4 +176,19 @@ mod tests {
"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");
}
}

View File

@ -177,7 +177,7 @@ pub type HkdfSha256 = Hkdf<sha256::Hash>;
pub type HkdfSha512 = Hkdf<sha512::Hash>;
/// A hashing engine which bytes can be serialized into.
pub trait HashEngine: Clone + Default {
pub trait HashEngine: Clone {
/// Length of the hash's internal block size, in bytes.
const BLOCK_SIZE: usize;
@ -189,6 +189,9 @@ pub trait HashEngine: Clone + Default {
}
/// 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
@ -196,13 +199,21 @@ pub trait GeneralHash: Hash {
type Engine: HashEngine;
/// Constructs a new engine.
fn engine() -> Self::Engine { Self::Engine::default() }
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 {
fn hash(data: &[u8]) -> Self
where
Self::Engine: Default,
{
let mut engine = Self::engine();
engine.input(data);
Self::from_engine(engine)
@ -213,6 +224,7 @@ pub trait GeneralHash: Hash {
where
B: AsRef<[u8]>,
I: IntoIterator<Item = B>,
Self::Engine: Default,
{
let mut engine = Self::engine();
for slice in byte_slices {

View File

@ -6,13 +6,13 @@ use core::ops::Index;
use core::slice::SliceIndex;
use core::{cmp, mem, ptr};
use crate::internal_macros::arr_newtype_fmt_impl;
use crate::HashEngine as _;
crate::internal_macros::hash_type! {
64,
false,
"Output of the SipHash24 hash function."
}
#[doc = "Output of the SipHash24 hash function."]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Hash([u8; 8]);
#[cfg(not(hashes_fuzz))]
fn from_engine(e: HashEngine) -> Hash { Hash::from_u64(Hash::from_engine_to_u64(e)) }
@ -106,10 +106,6 @@ impl HashEngine {
}
}
/// Creates a new SipHash24 engine.
#[inline]
pub const fn new() -> HashEngine { HashEngine::with_keys(0, 0) }
/// Retrieves the keys of this engine.
pub fn keys(&self) -> (u64, u64) { (self.k0, self.k1) }
@ -128,10 +124,6 @@ impl HashEngine {
}
}
impl Default for HashEngine {
fn default() -> Self { HashEngine::new() }
}
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 8;
@ -186,6 +178,9 @@ impl Hash {
Hash::from_engine(engine)
}
/// Produces a hash from the current state of a given engine.
pub fn from_engine(e: HashEngine) -> Hash { from_engine(e) }
/// Hashes the given data directly to u64 with an engine with the provided keys.
pub fn hash_to_u64_with_keys(k0: u64, k1: u64, data: &[u8]) -> u64 {
let mut engine = HashEngine::with_keys(k0, k1);
@ -215,7 +210,38 @@ impl Hash {
/// Creates a hash from its (little endian) 64-bit integer representation.
pub fn from_u64(hash: u64) -> Hash { Hash(hash.to_le_bytes()) }
fn from_slice(sl: &[u8]) -> Result<Self, crate::FromSliceError> {
let mut ret = [0; 8];
ret.copy_from_slice(sl);
Ok(Hash(ret))
}
}
impl crate::Hash for Hash {
type Bytes = [u8; 8];
const LEN: usize = 8;
const DISPLAY_BACKWARD: bool = false;
fn from_slice(sl: &[u8]) -> Result<Self, crate::FromSliceError> { Self::from_slice(sl) }
fn to_byte_array(self) -> Self::Bytes { self.0 }
fn as_byte_array(&self) -> &Self::Bytes { &self.0 }
fn from_byte_array(bytes: Self::Bytes) -> Self { Hash(bytes) }
}
impl<I: SliceIndex<[u8]>> Index<I> for Hash {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output { &self.0[index] }
}
arr_newtype_fmt_impl!(Hash, 8);
borrow_slice_impl!(Hash);
/// Load an u64 using up to 7 bytes of a byte slice.
///
@ -341,7 +367,7 @@ mod benches {
#[bench]
pub fn siphash24_1ki(bh: &mut Bencher) {
let mut engine = siphash24::Hash::engine();
let mut engine = siphash24::HashEngine::with_keys(0, 0);
let bytes = [1u8; 1024];
bh.iter(|| {
engine.input(&bytes);
@ -351,7 +377,7 @@ mod benches {
#[bench]
pub fn siphash24_64ki(bh: &mut Bencher) {
let mut engine = siphash24::Hash::engine();
let mut engine = siphash24::HashEngine::with_keys(0, 0);
let bytes = [1u8; 65536];
bh.iter(|| {
engine.input(&bytes);

View File

@ -30,7 +30,6 @@ impl_regression_test! {
regression_sha384, sha384, "f545bd83d297978d47a7f26b858a54188499dfb4d7d570a6a2362c765031d57a29d7e002df5e34d184e70b65a4f47153";
regression_sha512, sha512, "057d0a37e9e0ac9a93acde0752748da059a27bcf946c7af00692ac1a95db8d21f965f40af22efc4710f100f8d3e43f79f77b1f48e1e400a95b7344b7bc0dfd10";
regression_sha512_256, sha512_256, "e204244c429b5bca037a2a8a6e7ed8a42b808ceaff182560840bb8c5c8e9a2ec";
regression_siphash24, siphash24, "e823ed82311d601a";
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
@ -90,3 +89,14 @@ fn regression_hmac_sha512_with_key() {
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);
}