Skip to content

Commit

Permalink
Implements hashing as a blanket trait for instances of `CanonicalSeri…
Browse files Browse the repository at this point in the history
…alize`

- this saves an alllocation w.r.t the suggested approach by implementing `io::Write` on the input instance of `digest::Digest`,
- note that most instances of `digest::Digest` [already](https://gist.github.com/huitseeker/e827161413063e347ce5a496b66ff287) have an [`io::Write` instance](https://github.com/rustcrypto/hashes#hashing-readable-objects), but `CanonicalSerialize` consuming its `io::Write` argument prevents its usage,
- this hence implements `io::Write` on a [cheap newtype wrapper](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html)

Fixes #86
  • Loading branch information
huitseeker committed Apr 21, 2021
1 parent bfa7043 commit 48847ee
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
8 changes: 7 additions & 1 deletion serialize/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ edition = "2018"
[dependencies]
ark-serialize-derive = { version = "^0.2.0", path = "../serialize-derive", optional = true }
ark-std = { version = "0.2.0", default-features = false }
digest = { version = "0.9", default-features = false }

[dev-dependencies]
sha2 = { version = "0.9.3", default-features = false}
sha3 = { version = "0.9.1", default-features = false}
blake2 = { version = "0.9.1", default-features = false}

[features]
default = []
std = [ "ark-std/std" ]
std = [ "ark-std/std", ]
derive = [ "ark-serialize-derive" ]
63 changes: 63 additions & 0 deletions serialize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub use flags::*;
#[doc(hidden)]
pub use ark_serialize_derive::*;

use digest::{generic_array::GenericArray, Digest};

/// Serializer in little endian format allowing to encode flags.
pub trait CanonicalSerializeWithFlags: CanonicalSerialize {
/// Serializes `self` and `flags` into `writer`.
Expand Down Expand Up @@ -94,6 +96,37 @@ pub trait CanonicalSerialize {
}
}

// This private struct works around Serialize taking the pre-existing
// std::io::Write instance of most digest::Digest implementations by value
struct HashMarshaller<'a, H: Digest>(&'a mut H);

impl<'a, H: Digest> ark_std::io::Write for HashMarshaller<'a, H> {
#[inline]
fn write(&mut self, buf: &[u8]) -> ark_std::io::Result<usize> {
Digest::update(self.0, buf);
Ok(buf.len())
}

#[inline]
fn flush(&mut self) -> ark_std::io::Result<()> {
Ok(())
}
}

/// The Canonical serialization to bytes induces a natural way to hash the
/// corresponding value.
pub trait Hash: CanonicalSerialize {
fn hash<H: Digest>(&self) -> GenericArray<u8, <H as Digest>::OutputSize> {
let mut hasher = H::new();
self.serialize(HashMarshaller(&mut hasher))
.expect("HashMarshaller::flush should be infaillible!");
hasher.finalize()
}
}

// Nothing required in the extension -> derive it as a blanket impl
impl<T: CanonicalSerialize> Hash for T {}

/// Deserializer in little endian format allowing flags to be encoded.
pub trait CanonicalDeserializeWithFlags: Sized {
/// Reads `Self` and `Flags` from `reader`.
Expand Down Expand Up @@ -897,6 +930,18 @@ mod test {
assert_eq!(data, de);
}

fn test_hash<T: CanonicalSerialize, H: Digest + core::fmt::Debug>(data: T) {
let h1 = data.hash::<H>();

let mut hash = H::new();
let mut serialized = vec![0; data.serialized_size()];
data.serialize(&mut serialized[..]).unwrap();
hash.update(&serialized);
let h2 = hash.finalize();

assert_eq!(h1, h2);
}

// Serialize T, randomly mutate the data, and deserialize it.
// Ensure it fails.
// Up to the caller to provide a valid mutation criterion
Expand Down Expand Up @@ -1024,4 +1069,22 @@ mod test {
fn test_phantomdata() {
test_serialize(core::marker::PhantomData::<Dummy>);
}

#[test]
fn test_sha2() {
test_hash::<_, sha2::Sha256>(Dummy);
test_hash::<_, sha2::Sha512>(Dummy);
}

#[test]
fn test_blake2() {
test_hash::<_, blake2::Blake2b>(Dummy);
test_hash::<_, blake2::Blake2s>(Dummy);
}

#[test]
fn test_sha3() {
test_hash::<_, sha3::Sha3_256>(Dummy);
test_hash::<_, sha3::Sha3_512>(Dummy);
}
}

0 comments on commit 48847ee

Please sign in to comment.