Skip to content

Commit

Permalink
Implement hashing as a blanket trait for instances of `CanonicalSeria…
Browse files Browse the repository at this point in the history
…lize` (#265)

* Implements hashing as a blanket trait for instances of `CanonicalSerialize`

- 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

* Adjust post review

- rename Hash -> CanonicalSerialize Ext according to [extension trait best practices](https://rust-lang.github.io/rfcs/0445-extension-trait-conventions.html#the-convention)
- add the same structure for hashing uncompressed bytes

* Changelog entries for #263, #265

* Update CHANGELOG.md

Co-authored-by: Weikeng Chen <w.k@berkeley.edu>
  • Loading branch information
huitseeker and weikengchen authored Apr 23, 2021
1 parent 68d4347 commit 2b35d51
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- [\#245](https://github.com/arkworks-rs/algebra/pull/245) (ark-poly) Speedup the sequential and parallel radix-2 FFT and IFFT significantly by making the method in which it accesses roots more cache-friendly.
- [\#258](https://github.com/arkworks-rs/algebra/pull/258) (ark-poly) Add `Mul<F>` implementation for `DensePolynomial`.
- [\#261](https://github.com/arkworks-rs/algebra/pull/261) (ark-ff) Add support for 448-bit integers and fields.
- [\#263](https://github.com/arkworks-rs/algebra/pull/263) (ark-ff) Add `From<iXXX>` implementations to fields.
- [\#265](https://github.com/arkworks-rs/algebra/pull/265) (ark-serialize) Add hashing as an extension trait of `CanonicalSerialize`.

### Improvements

Expand Down
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" ]
81 changes: 81 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,45 @@ 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 CanonicalSerialize induces a natural way to hash the
/// corresponding value, of which this is the convenience trait.
pub trait CanonicalSerializeHashExt: 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()
}

fn hash_uncompressed<H: Digest>(&self) -> GenericArray<u8, <H as Digest>::OutputSize> {
let mut hasher = H::new();
self.serialize_uncompressed(HashMarshaller(&mut hasher))
.expect("HashMarshaller::flush should be infaillible!");
hasher.finalize()
}
}

/// CanonicalSerializeHashExt is a (blanket) extension trait of
/// CanonicalSerialize
impl<T: CanonicalSerialize> CanonicalSerializeHashExt 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 +938,28 @@ 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);

let h3 = data.hash_uncompressed::<H>();

let mut hash = H::new();
serialized = vec![0; data.uncompressed_size()];
data.serialize_uncompressed(&mut serialized[..]).unwrap();
hash.update(&serialized);
let h4 = hash.finalize();

assert_eq!(h3, h4);
}

// 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 +1087,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 2b35d51

Please sign in to comment.