-
Notifications
You must be signed in to change notification settings - Fork 29
Rust implementation #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tarcieri
wants to merge
61
commits into
benlaurie:master
Choose a base branch
from
tarcieri:rust
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
61 commits
Select commit
Hold shift + click to select a range
506ef6c
Initial commit
tarcieri 5fa5127
Fix README.md banner
tarcieri bad137e
Prep for 0.1.0 release
tarcieri 8cbc883
Flag features that depend on a hasher backend
tarcieri c1dc277
Fix extra colon in README
tarcieri c74ace0
Temporarily comment out *ring* dependency
tarcieri ac291f2
Add version constraint on rustc-serialize
tarcieri a41f626
Revert "Temporarily comment out *ring* dependency"
tarcieri 8cd17f7
Fix crate links in README.md
tarcieri e9b73a8
Remove Vec<u8> from ObjectHash trait
tarcieri 608ae25
Merge pull request #1 from cryptosphere/remove-vecs-from-trait
tarcieri 88e1fff
str support
tarcieri 396e451
Merge pull request #2 from cryptosphere/str
tarcieri 6c8c909
Remove redundant test
tarcieri 6e57d24
Vec<T> support
tarcieri 73947c4
Merge pull request #3 from cryptosphere/vectors
tarcieri f944a82
Make objecthash-ring a default feature
tarcieri 95522fe
Merge pull request #4 from cryptosphere/ring-by-default
tarcieri 2102d28
Export objecthash_digest! macro
tarcieri 067d147
Add comment about std::hash::BuildHasherDefault
tarcieri 0c41fa8
Add #[inline] to all implementations
tarcieri af17ea5
[u8] support
tarcieri 82e511d
Merge pull request #5 from cryptosphere/octet-strings
tarcieri 30c3d76
Hasher::write -> update, update_nested
tarcieri fc9ee65
Merge pull request #6 from cryptosphere/update-nested
tarcieri 216a8e6
Digest type
tarcieri 5992ee7
Merge pull request #7 from cryptosphere/zero-copy
tarcieri db2a167
HashMap (and String) support
tarcieri ff77df6
Merge pull request #8 from cryptosphere/hashmap-and-string
tarcieri 648f3db
Make 'types' module public
tarcieri d9b0c9d
Use the *ring* crate
tarcieri 4a22e21
Bump version to 0.1.1 and update CHANGES.md
tarcieri 2d63b26
Fix README URLs
tarcieri 2da0d75
objecthash_struct! and objecthash_dict_entry! macros
tarcieri a02ea55
Merge pull request #9 from cryptosphere/struct-macros
tarcieri 3e22897
Bump version to 0.2.0 and update CHANGES.md
tarcieri a3c0f46
Fix objecthash_struct macro
tarcieri f6a9163
Bump version to 0.2.1 and update CHANGES.md
tarcieri c078c87
Coerce struct key names to Strings
tarcieri 10cd417
Bump version to 0.2.2 and update CHANGES.md
tarcieri bf5d415
objecthash_struct_member! macro
tarcieri a1ff822
Bump version to 0.3.0 and update CHANGES.md
tarcieri 964a0e9
Support digests of dynamically sized types
tarcieri 65f3c5c
Merge pull request #10 from cryptosphere/dst-support
tarcieri 85e0833
Always use Digest newtype
tarcieri d92bfe6
Merge pull request #11 from cryptosphere/digest-newtype
tarcieri 258ad23
Add DANGER: EXPERIMENTAL to README
tarcieri a32457e
New objecthash_member! macro
tarcieri 1f0ca0a
Run tests on nightly/Linux only
tarcieri aba91f9
Merge pull request #12 from cryptosphere/simplify-macros
tarcieri 801aec8
Macro fixups: take 2
tarcieri a161564
Merge pull request #13 from cryptosphere/macros-take-2
tarcieri ded97c0
Bump version to 0.4.0 and update CHANGES.md
tarcieri 8a61c21
Implement Default for ring::Hasher
tarcieri 6ef88cb
Minor clippy fixups
tarcieri 11ea46e
Merge pull request #14 from cryptosphere/clippy-fixups
tarcieri 4eb09d9
Bump version to 0.4.1 and update CHANGES.md
tarcieri 5492123
Better emphasize link back to the original project
tarcieri c2cec3e
rust: Prepare to merge into benlaurie/objecthash
tarcieri 0c032a5
Merge remote-tracking branch 'objecthash-rs/merge-prep' into rust
tarcieri f0f7b2c
.travis.yml: Rust configuration
tarcieri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,3 +16,8 @@ matrix: | |
| - cd ruby | ||
| - bundle | ||
| - bundle exec rake | ||
| - language: rust | ||
| rust: stable | ||
| script: | ||
| - cd rust | ||
| - cargo test | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| ## 0.4.1 (2017-01-16) | ||
|
|
||
| * Clippy fixups | ||
|
|
||
| ## 0.4.0 (2017-01-16) | ||
|
|
||
| * Rename `objecthash_struct_member!` to `objecthash_member!` | ||
| * Do not automatically create references from objecthash macro args | ||
| * Remove associated digest type from ObjectHasher trait. Always use | ||
| objecthash::Digest instead | ||
|
|
||
| ## 0.3.0 (2016-08-21) | ||
|
|
||
| * `objecthash_struct_member!` macro | ||
|
|
||
| ## 0.2.2 (2016-08-21) | ||
|
|
||
| * Coerce `objecthash_struct!` key names to Strings | ||
|
|
||
| ## 0.2.1 (2016-08-21) | ||
|
|
||
| * Bugfix for `objecthash_struct!` macro | ||
|
|
||
| ## 0.2.0 (2016-08-21) | ||
|
|
||
| * Add `objecthash_struct!` and `objecthash_dict_entry!` macros | ||
|
|
||
| ## 0.1.1 (2016-08-15) | ||
|
|
||
| * Add the newly released *ring* crate as an optional dependency | ||
|
|
||
| ## 0.1.0 (2016-08-08) | ||
|
|
||
| * Initial release | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| [package] | ||
| name = "objecthash" | ||
| version = "0.4.1" | ||
| description = "A content hashing algorithm which works across multiple encodings (JSON, Protobufs, etc)" | ||
| homepage = "https://github.com/cryptosphere/rust-objecthash" | ||
| repository = "https://github.com/cryptosphere/rust-objecthash" | ||
| readme = "README.md" | ||
| keywords = ["hash", "digest", "signatures", "Merkle", "blockchain"] | ||
| license = "Apache-2.0" | ||
| authors = ["Tony Arcieri <bascule@gmail.com>"] | ||
|
|
||
| [dependencies.unicode-normalization] | ||
| version = ">= 0.1.2" | ||
|
|
||
| [dependencies.ring] | ||
| optional = true | ||
| version = ">= 0.2" | ||
|
|
||
| [dev-dependencies.rustc-serialize] | ||
| version = ">= 0.3.19" | ||
|
|
||
| [features] | ||
| default = ["objecthash-ring"] | ||
| objecthash-ring = ["ring"] | ||
| octet-strings = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| # ObjectHash for Rust [![Latest Version][crate-image]][crate-link] [![Build Status][build-image]][build-link] [![Apache 2 licensed][license-image]][license-link] | ||
|
|
||
| [crate-image]: https://img.shields.io/crates/v/objecthash.svg | ||
| [crate-link]: https://crates.io/crates/objecthash | ||
| [build-image]: https://travis-ci.org/cryptosphere/objecthash-rs.svg?branch=master | ||
| [build-link]: https://travis-ci.org/cryptosphere/objecthash-rs | ||
| [license-image]: https://img.shields.io/badge/license-Apache2-blue.svg | ||
| [license-link]: https://github.com/cryptosphere/objecthash-rs/blob/master/LICENSE | ||
|
|
||
| A content hash algorithm which works across multiple encodings (JSON, Protobufs, etc). | ||
|
|
||
| This crate provides a Rust implementation of an algorithm originally created by Ben Laurie: | ||
|
|
||
| https://github.com/benlaurie/objecthash | ||
|
|
||
| ### Is it any good? | ||
|
|
||
| [Yes.](http://news.ycombinator.com/item?id=3067434) | ||
|
|
||
| ### Is it "Production Ready™"? | ||
|
|
||
|  | ||
|
|
||
| **No!** ObjectHash is an *experimental* algorithm, and is subject to change. Please do not depend on it yet. | ||
|
|
||
| Additionally, this is a project of a cryptographic nature and has not received any expert review. | ||
|
|
||
| Use at your own risk. | ||
|
|
||
| ## Installation | ||
|
|
||
| You will need to select a supported cryptography library to use as ObjectHash's backend. The following backend libraries | ||
| are supported: | ||
|
|
||
| * [ring]: A safe, fast, small Rust crypto library based on BoringSSL's cryptography primitives | ||
|
|
||
| [ring]: https://github.com/briansmith/ring | ||
|
|
||
| Please make sure to add a crypto backend crate or the `objecthash` crate will not work! | ||
|
|
||
| ## Usage | ||
|
|
||
| ObjectHashes can be used to compute a content hash of a deeply nested structure. The intended use is to first | ||
| deserialize data into a nested structure, then perform an ObjectHash digest of its contents. This way, the same | ||
| content hash to be computed regardless of how the data is serialized, which allows the data to be transcoded between | ||
| formats without having to recompute the content hash. | ||
|
|
||
| This crate defines a trait called ObjectHash: | ||
|
|
||
| ```rust | ||
| pub trait ObjectHash { | ||
| fn objecthash<H: ObjectHasher>(&self, hasher: &mut H); | ||
| } | ||
| ``` | ||
|
|
||
| There are built-in implementations of the `ObjectHash` trait for the | ||
| following types: | ||
|
|
||
| * `Vec<T: ObjectHash>` | ||
| * `HashMap<K: ObjectHash, V: ObjectHash>` | ||
| * `str` | ||
| * `String` | ||
| * **Integers:** | ||
| * `i8` | ||
| * `i16` | ||
| * `i32` | ||
| * `i64` | ||
| * `u8` | ||
| * `u16` | ||
| * `u32` | ||
| * `u64` | ||
| * `isize` | ||
| * `usize` | ||
|
|
||
| To calculate the ObjectHash digest of some data, call the following: | ||
|
|
||
| ```rust | ||
| let digest: Vec<u8> = objecthash::digest(42); | ||
| ``` | ||
|
|
||
| This will compute a digest (using the SHA-256 algorithm) of the given value, provided the type of the value given | ||
| implements the ObjectHash trait. | ||
|
|
||
|
|
||
| ## Macros | ||
|
|
||
| The `objecthash_struct!` macro is designed to simplify implementing the ObjectHash trait on structs, producing | ||
| a dict-type hash across their keys and values: | ||
|
|
||
| ```rust | ||
| impl ObjectHash for MyStruct { | ||
| #[inline] | ||
| fn objecthash<H: ObjectHasher>(&self, hasher: &mut H) { | ||
| objecthash_struct!( | ||
| hasher, | ||
| "foo" => self.foo, | ||
| "bar" => self.bar, | ||
| "baz" => self.baz | ||
| ) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## TODO | ||
|
|
||
| * More types | ||
| * More test vectors | ||
| * Redaction support | ||
|
|
||
| ## Contributing | ||
|
|
||
| * Fork this repository on Github | ||
| * Make your changes and send a pull request | ||
| * If your changes look good, we'll merge them | ||
|
|
||
| ## Copyright | ||
|
|
||
| Copyright (c) 2016-2017 Tony Arcieri. Distributed under the Apache 2.0 License. | ||
| See LICENSE file for further details. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| #[cfg(feature = "objecthash-ring")] | ||
| pub mod ring; | ||
|
|
||
| // TODO: Use std::hash::BuildHasherDefault or our own similar version | ||
| #[cfg(feature = "objecthash-ring")] | ||
| pub fn default() -> ring::Hasher { | ||
| ring::Hasher::default() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| extern crate ring; | ||
|
|
||
| use Digest; | ||
| use ObjectHasher; | ||
|
|
||
| pub struct Hasher { | ||
| ctx: ring::digest::Context, | ||
| } | ||
|
|
||
| impl Hasher { | ||
| pub fn new(alg: &'static ring::digest::Algorithm) -> Hasher { | ||
| Hasher { ctx: ring::digest::Context::new(alg) } | ||
| } | ||
| } | ||
|
|
||
| impl Default for Hasher { | ||
| fn default() -> Self { | ||
| Self::new(&ring::digest::SHA256) | ||
| } | ||
| } | ||
|
|
||
| impl ObjectHasher for Hasher { | ||
| #[inline] | ||
| fn output_len(&self) -> usize { | ||
| self.ctx.algorithm.output_len | ||
| } | ||
|
|
||
| #[inline] | ||
| fn update(&mut self, bytes: &[u8]) { | ||
| self.ctx.update(bytes); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn update_nested<F>(&mut self, nested: F) | ||
| where F: Fn(&mut Self) | ||
| { | ||
| let mut nested_hasher = Hasher::new(self.ctx.algorithm); | ||
| nested(&mut nested_hasher); | ||
| self.update(nested_hasher.finish().as_ref()); | ||
| } | ||
|
|
||
| #[inline] | ||
| fn finish(self) -> Digest { | ||
| Digest::new(self.ctx.finish().as_ref()).unwrap() | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::Hasher; | ||
| use ObjectHasher; | ||
| use rustc_serialize::hex::ToHex; | ||
|
|
||
| // From Project NESSIE | ||
| // https://www.cosic.esat.kuleuven.be/nessie/testvectors/hash/sha/Sha-2-256.unverified.test-vectors | ||
| const SHA256_VECTOR_STRING: &'static str = "abcdefghijklmnopqrstuvwxyz"; | ||
| const SHA256_VECTOR_DIGEST: &'static str = "71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73"; | ||
|
|
||
| #[test] | ||
| fn sha256() { | ||
| let mut hasher = Hasher::default(); | ||
| hasher.update(SHA256_VECTOR_STRING.as_bytes()); | ||
| assert_eq!(hasher.finish().as_ref().to_hex(), SHA256_VECTOR_DIGEST); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| extern crate unicode_normalization; | ||
|
|
||
| #[cfg(test)] | ||
| extern crate rustc_serialize; | ||
|
|
||
| #[macro_use] | ||
| pub mod macros; | ||
|
|
||
| pub mod hasher; | ||
| pub mod types; | ||
|
|
||
| const MAX_OUTPUT_LEN: usize = 32; | ||
|
|
||
| pub struct Digest { | ||
| output_len: usize, | ||
| value: [u8; MAX_OUTPUT_LEN], | ||
| } | ||
|
|
||
| impl Digest { | ||
| pub fn new(bytes: &[u8]) -> Result<Digest, ()> { | ||
| if bytes.len() > MAX_OUTPUT_LEN { | ||
| return Err(()); | ||
| } | ||
|
|
||
| let mut digest_bytes = [0u8; MAX_OUTPUT_LEN]; | ||
| digest_bytes.copy_from_slice(bytes); | ||
|
|
||
| Ok(Digest { | ||
| output_len: bytes.len(), | ||
| value: digest_bytes, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| impl AsRef<[u8]> for Digest { | ||
| #[inline] | ||
| fn as_ref(&self) -> &[u8] { | ||
| &self.value[..self.output_len] | ||
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "objecthash-ring")] | ||
| pub fn digest<T: ObjectHash + ?Sized>(msg: &T) -> Digest { | ||
| let mut hasher = hasher::default(); | ||
| msg.objecthash(&mut hasher); | ||
| hasher.finish() | ||
| } | ||
|
|
||
| pub trait ObjectHasher { | ||
| fn output_len(&self) -> usize; | ||
| fn update(&mut self, bytes: &[u8]); | ||
| fn update_nested<F>(&mut self, nested: F) where F: Fn(&mut Self); | ||
| fn finish(self) -> Digest; | ||
| } | ||
|
|
||
| pub trait ObjectHash { | ||
| fn objecthash<H: ObjectHasher>(&self, hasher: &mut H); | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| #[cfg(feature = "objecthash-ring")] | ||
| mod tests { | ||
| use digest; | ||
| use rustc_serialize::hex::ToHex; | ||
|
|
||
| #[test] | ||
| fn digest_test() { | ||
| let result = digest(&1000); | ||
| assert_eq!(result.as_ref().to_hex(), | ||
| "a3346d18105ef801c3598fec426dcc5d4be9d0374da5343f6c8dcbdf24cb8e0b"); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #[macro_export] | ||
| macro_rules! objecthash_member { | ||
| ($key:expr => $value:expr) => { | ||
| { | ||
| let key_digest = $crate::digest($key); | ||
| let value_digest = $crate::digest($value); | ||
| let mut result = Vec::with_capacity(key_digest.as_ref().len() + value_digest.as_ref().len()); | ||
|
|
||
| result.extend_from_slice(key_digest.as_ref()); | ||
| result.extend_from_slice(value_digest.as_ref()); | ||
| result | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[macro_export] | ||
| macro_rules! objecthash_struct( | ||
| { $hasher:expr, $($key:expr => $value:expr),+ } => { | ||
| { | ||
| let mut digests: Vec<Vec<u8>> = Vec::new(); | ||
|
|
||
| $( | ||
| digests.push(objecthash_member!($key => $value)); | ||
| )+ | ||
|
|
||
| digests.sort(); | ||
|
|
||
| $hasher.update($crate::types::DICT_TAG); | ||
| for value in &digests { | ||
| $hasher.update(&value); | ||
| } | ||
| } | ||
| }; | ||
| ); | ||
|
|
||
| #[cfg(test)] | ||
| #[cfg(feature = "objecthash-ring")] | ||
| mod tests { | ||
| use {hasher, ObjectHasher}; | ||
| use rustc_serialize::hex::ToHex; | ||
|
|
||
| #[test] | ||
| fn objecthash_struct_test() { | ||
| let mut h = hasher::default(); | ||
|
|
||
| objecthash_struct!(h, "foo" => &1); | ||
|
|
||
| assert_eq!( | ||
| h.finish().as_ref().to_hex(), | ||
| "bf4c58f5e308e31e2cd64bdbf7a01b9b595a13602438be5e912c7d94f6d8177a" | ||
| ); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think ideally this would be replaced with a procedural macro, possibly even leveraging serde visitors