From 5b2d87b9e2d5ae4de6ed714a56d3cdf7c9b55a17 Mon Sep 17 00:00:00 2001 From: Martin Larralde Date: Thu, 20 Jul 2017 14:14:34 +0200 Subject: [PATCH] Implement Hasher trait for all sums --- CHANGELOG.md | 2 +- README.md | 52 +++++++++++++++++++++++------------------------ src/adler32.rs | 34 ++++++++++++------------------- src/bsd.rs | 28 +++++++++++++------------ src/crc32.rs | 33 +++++++++++++----------------- src/crc32c.rs | 35 +++++++++++++------------------ src/fletcher16.rs | 32 +++++++++++++++-------------- src/sysv.rs | 35 ++++++++++++++++--------------- 8 files changed, 117 insertions(+), 134 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0055e7d..5a54de3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Unreleased ### Added -- Implementation of the following sums: +- Following sums with Hasher & Digest implementations: * Adler32 * BSD checksum (UNIX `sum`) * CRC32 (Ethernet variant) diff --git a/README.md b/README.md index a9b8c76..c5fb366 100644 --- a/README.md +++ b/README.md @@ -21,54 +21,54 @@ pruefung = "^0.1.0" Check out the **Sums** section to see the minimal required version you need depending on the algorithm you wish to use. -All the checksums are implemented using the same logic -as the [hashes](https://github.com/RustCrypto/hashes) -crate of the [RustCrypto](https://github.com/RustCrypto) -project, implementing the [`digest::Digest`](https://docs.rs/digest/0.6.1/digest/trait.Digest.html) trait. +All the checksums are implemented using the same logic as the [hashes] crate of +the [RustCrypto] project, implementing the [`digest::Digest`] and the +[`core::hasher::Hasher`] traits. Then, to compute a hash, for instance a `CRC32` (Ethernet standard): ```rust extern crate pruefung; +use std::hash::Hasher; + let mut hasher = pruefung::crc32::CRC32(); // Instantiate a hasher let data = b"Hello, world !"; -hasher.input(data); // Feed the hasher -hasher.input("String data".as_bytes()); // (possibly multiple times) +hasher.write(data); // Feed the hasher +hasher.write("String data".as_bytes()); // (possibly multiple times) -let hash = hasher.result(); // Consume the hasher +let hash = hasher.finsh(); // Consume the hasher println!("Result: {:x}", hash) // print the result as native hex ``` + ## Sums Latest version of the crate implements the following checksums: - -Algorithm | since version ---------------- | ------------- -[Adler32] | `0.1.0` -[BSD checksum] | `0.1.0` -[CRC32] | `0.1.0` -[CRC32C] | `0.1.0` -[Fletcher16] | `0.1.0` -[SysV checksum] | `0.1.0` - -[Adler32]: https://en.wikipedia.org/wiki/Adler-32 -[BSD checksum]: https://en.wikipedia.org/wiki/BSD_checksum -[CRC32]: https://en.wikipedia.org/wiki/Cyclic_redundancy_check -[CRC32C]: https://en.wikipedia.org/wiki/Cyclic_redundancy_check -[Fletcher16]: https://en.wikipedia.org/wiki/Fletcher%27s_checksum -[SysV checksum]: https://en.wikipedia.org/wiki/SYSV_checksum +Algorithm | *since* +----------------------------------------------------------------- | ------- +[Adler32](https://en.wikipedia.org/wiki/Adler-32) | `0.1.0` +[BSD checksum](https://en.wikipedia.org/wiki/BSD_checksum) | `0.1.0` +[CRC32](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) | `0.1.0` +[CRC32C](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) | `0.1.0` +[Fletcher16](https://en.wikipedia.org/wiki/Fletcher%27s_checksum) | `0.1.0` +[SysV checksum](https://en.wikipedia.org/wiki/SYSV_checksum) | `0.1.0` These checksums are **NOT** cryptographically secure. They should not be used for something else than data validation against *accidental* modifications: an attacker could easily *forge* a file to pass any of these checksums ! For -secure checksums, look at the [hashes](https://github.com/RustCrypto/hashes) -implemented by the RustCrypto developers. +secure checksums, look at the [hashes] implemented by the [RustCrypto] team. + ## Why `pruefung` ? *I was in Switzerland when I started this project. Yet, I don't really speak german. But a slug version of `zyklische-redundanzprüfung` seemed like a nice -name, instead of another checksum, cksum, checksums, etc, crc crate.* +name, instead of another checksum, cksum, checksums, crc, etc. crate.* + + +[hashes]: https://github.com/RustCrypto/hashes +[RustCrypto]: https://github.com/RustCrypto +[`digest::Digest`]: https://docs.rs/digest/0.6.1/digest/trait.Digest.html +[`core::hasher::Hasher`]: https://doc.rust-lang.org/core/hash/trait.Hasher.html diff --git a/src/adler32.rs b/src/adler32.rs index 85e121e..f99e312 100644 --- a/src/adler32.rs +++ b/src/adler32.rs @@ -4,6 +4,7 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; use core::borrow::BorrowMut; @@ -30,29 +31,14 @@ impl Default for Adler32 { } -impl Adler32 { +impl Hasher for Adler32 { #[inline] - fn finalize(self) -> [u32; 2]{ - [ - self.sum1 % consts::BASE, - self.sum2 % consts::BASE, - ] - } - - #[inline] - pub fn hash(self) -> u32 { - let sums = self.finalize(); - (sums[1] << 16) | sums[0] - } - - #[inline] - pub fn consume(&mut self, input: &[u8]) { + fn write(&mut self, input: &[u8]) { let mut byte_it = input.iter(); let mut i: usize; loop { - i = 0; // Read bytes by block of NMAX (max value before u16 overflow) for &byte in byte_it.borrow_mut().take(consts::NMAX) { @@ -69,6 +55,11 @@ impl Adler32 { if i < consts::NMAX {break;} } } + + #[inline] + fn finish(&self) -> u64 { + (((self.sum2 % consts::BASE) << 16) | self.sum1) as u64 + } } @@ -81,7 +72,7 @@ impl digest::BlockInput for Adler32 { impl digest::Input for Adler32 { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -92,7 +83,7 @@ impl digest::FixedOutput for Adler32 { #[inline] fn fixed_result(self) -> generic_array::GenericArray { let mut out = generic_array::GenericArray::default(); - byte_tools::write_u32_be(&mut out, self.hash()); + byte_tools::write_u32_be(&mut out, self.finish() as u32); out } } @@ -102,6 +93,7 @@ impl digest::FixedOutput for Adler32 { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; use digest::Input; use digest::FixedOutput; @@ -112,7 +104,7 @@ mod tests { let adler = super::Adler32::new(); let output: [u8; 4] = [0, 0, 0, 1]; - assert!(adler.hash() == 1); + assert!(adler.finish() == 1); assert!(adler.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -127,6 +119,6 @@ mod tests { adler1.process(&data[3..]); adler2.process(&data[..]); - assert!(adler1.hash() == adler2.hash()); + assert!(adler1.finish() == adler2.finish()); } } diff --git a/src/bsd.rs b/src/bsd.rs index 5554346..c0e4c5b 100644 --- a/src/bsd.rs +++ b/src/bsd.rs @@ -4,6 +4,8 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; + mod consts { pub const SIZE: usize = 16; @@ -26,21 +28,20 @@ impl Default for BSD { } -impl BSD { - - #[inline] - pub fn hash(self) -> u16 { - self.state as u16 - } - +impl Hasher for BSD { #[inline] - pub fn consume(&mut self, input: &[u8]) { + fn write(&mut self, input: &[u8]) { for &byte in input.iter() { // Rotate one bit right, add next byte an prevent overflow with mask self.state = (self.state >> 1) + ((self.state & 1) << (consts::SIZE - 1)); self.state = (self.state + (byte as u32)) & consts::BASE; } } + + #[inline] + fn finish(&self) -> u64 { + self.state as u64 + } } @@ -53,7 +54,7 @@ impl digest::BlockInput for BSD { impl digest::Input for BSD { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -75,6 +76,7 @@ impl digest::FixedOutput for BSD { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; use digest::Input; use digest::FixedOutput; @@ -85,7 +87,7 @@ mod tests { let bsd = super::BSD::new(); let output: [u8; 2] = [0, 0]; - assert!(bsd.hash() == 0); + assert!(bsd.finish() == 0); assert!(bsd.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -94,9 +96,9 @@ mod tests { let mut bsd = super::BSD::new(); let output: [u8; 2] = [0, 'a' as u8]; - bsd.consume("a".as_bytes()); + bsd.write("a".as_bytes()); - assert!(bsd.hash() == 'a' as u16); + assert!(bsd.finish() == 'a' as u64); assert!(bsd.fixed_result() == GenericArray::clone_from_slice(&output)) } @@ -110,6 +112,6 @@ mod tests { bsd1.process(&data[3..]); bsd2.process(&data[..]); - assert!(bsd1.hash() == bsd2.hash()); + assert!(bsd1.finish() == bsd2.finish()); } } diff --git a/src/crc32.rs b/src/crc32.rs index 7025895..d1cb166 100644 --- a/src/crc32.rs +++ b/src/crc32.rs @@ -4,6 +4,8 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; + mod consts { pub const LOOKUP_TABLE: [u32; 256] = [ @@ -94,21 +96,9 @@ impl Default for CRC32 { } } - -impl CRC32 { - #[inline] - fn finalize(self) -> u32 { - self.state - } - - #[inline] - pub fn hash(self) -> u32 { - self.state - } - +impl Hasher for CRC32 { #[inline] - pub fn consume(&mut self, input: &[u8]) { - + fn write(&mut self, input: &[u8]) { let mut crc = !self.state; let mut pos: u32; @@ -119,6 +109,11 @@ impl CRC32 { self.state = !crc; } + + #[inline] + fn finish(&self) -> u64 { + self.state as u64 + } } @@ -131,7 +126,7 @@ impl digest::BlockInput for CRC32 { impl digest::Input for CRC32 { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -142,7 +137,7 @@ impl digest::FixedOutput for CRC32 { #[inline] fn fixed_result(self) -> generic_array::GenericArray { let mut out = generic_array::GenericArray::default(); - byte_tools::write_u32_be(&mut out, self.finalize()); + byte_tools::write_u32_be(&mut out, self.finish() as u32); out } } @@ -152,6 +147,7 @@ impl digest::FixedOutput for CRC32 { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; use digest::Input; use digest::FixedOutput; @@ -163,8 +159,7 @@ mod tests { let output: [u8; 4] = [0, 0, 0, 0]; - assert!(crc.hash() == 0); - assert!(crc.finalize() == 0); + assert!(crc.finish() == 0); assert!(crc.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -179,6 +174,6 @@ mod tests { crc1.process(&data[3..]); crc2.process(&data[..]); - assert!(crc1.hash() == crc2.hash()); + assert!(crc1.finish() == crc2.finish()); } } diff --git a/src/crc32c.rs b/src/crc32c.rs index 79ec90f..b1dc16e 100644 --- a/src/crc32c.rs +++ b/src/crc32c.rs @@ -4,6 +4,8 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; + mod consts { pub const LOOKUP_TABLE: [u32; 256] = [ @@ -94,21 +96,9 @@ impl Default for CRC32C { } } - -impl CRC32C { - #[inline] - fn finalize(self) -> u32 { - self.state - } - - #[inline] - pub fn hash(self) -> u32 { - self.state - } - +impl Hasher for CRC32C { #[inline] - pub fn consume(&mut self, input: &[u8]) { - + fn write(&mut self, input: &[u8]) { let mut crc = !self.state; let mut pos: u32; @@ -119,8 +109,12 @@ impl CRC32C { self.state = !crc; } -} + #[inline] + fn finish(&self) -> u64 { + self.state as u64 + } +} #[cfg(feature = "generic")] impl digest::BlockInput for CRC32C { @@ -131,7 +125,7 @@ impl digest::BlockInput for CRC32C { impl digest::Input for CRC32C { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -142,7 +136,7 @@ impl digest::FixedOutput for CRC32C { #[inline] fn fixed_result(self) -> generic_array::GenericArray { let mut out = generic_array::GenericArray::default(); - byte_tools::write_u32_be(&mut out, self.finalize()); + byte_tools::write_u32_be(&mut out, self.finish() as u32); out } } @@ -152,6 +146,7 @@ impl digest::FixedOutput for CRC32C { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; use digest::Input; use digest::FixedOutput; @@ -162,9 +157,7 @@ mod tests { let crc = super::CRC32C::new(); let output: [u8; 4] = [0, 0, 0, 0]; - - assert!(crc.hash() == 0); - assert!(crc.finalize() == 0); + assert!(crc.finish() == 0); assert!(crc.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -179,6 +172,6 @@ mod tests { crc1.process(&data[3..]); crc2.process(&data[..]); - assert!(crc1.hash() == crc2.hash()); + assert!(crc1.finish() == crc2.finish()); } } diff --git a/src/fletcher16.rs b/src/fletcher16.rs index 52eaf0e..0a64753 100644 --- a/src/fletcher16.rs +++ b/src/fletcher16.rs @@ -4,6 +4,7 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; use core::borrow::BorrowMut; @@ -32,22 +33,16 @@ impl Default for Fletcher16 { impl Fletcher16 { #[inline] - fn finalize(self) -> [u16; 2]{ - [ - (self.sum1 & consts::BASE) + (self.sum1 >> 8), - (self.sum2 & consts::BASE) + (self.sum2 >> 8), - ] + fn finalize(&self) -> [u16; 2] { + [(self.sum1 & consts::BASE) + (self.sum1 >> 8), + (self.sum2 & consts::BASE) + (self.sum2 >> 8)] } +} - #[inline] - pub fn hash(self) -> u16 { - let sums = self.finalize(); - (sums[1] << 8) | sums[0] - } +impl Hasher for Fletcher16 { #[inline] - pub fn consume(&mut self, input: &[u8]) { - + fn write (&mut self, input: &[u8]) { let mut byte_it = input.iter(); let mut i: usize; @@ -69,6 +64,12 @@ impl Fletcher16 { if i < consts::NMAX {break;} } } + + #[inline] + fn finish(&self) -> u64 { + let sums = self.finalize(); + ((sums[1] << 8) | sums[0]) as u64 + } } @@ -81,7 +82,7 @@ impl digest::BlockInput for Fletcher16 { impl digest::Input for Fletcher16 { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -104,6 +105,7 @@ impl digest::FixedOutput for Fletcher16 { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; use digest::Input; use digest::FixedOutput; @@ -114,7 +116,7 @@ mod tests { let fletcher = super::Fletcher16::new(); let output: [u8; 2] = [0, 0]; - assert!(fletcher.hash() == 0); + assert!(fletcher.finish() == 0); assert!(fletcher.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -129,6 +131,6 @@ mod tests { fletcher1.process(&data[3..]); fletcher2.process(&data[..]); - assert!(fletcher1.hash() == fletcher2.hash()); + assert!(fletcher1.finish() == fletcher2.finish()); } } diff --git a/src/sysv.rs b/src/sysv.rs index 4189af6..cbaa1ab 100644 --- a/src/sysv.rs +++ b/src/sysv.rs @@ -4,6 +4,8 @@ extern crate generic_array; #[cfg(feature = "generic")] extern crate digest; +use core::hash::Hasher; + mod consts { pub const SIZE: usize = 16; @@ -25,22 +27,19 @@ impl Default for SysV { } } - -impl SysV { - - #[inline] - pub fn hash(self) -> u16 { - self.state as u16 - } - +impl Hasher for SysV { #[inline] - pub fn consume(&mut self, input: &[u8]) { + fn write(&mut self, input: &[u8]) { for &byte in input.iter() { // Add the byte to the checksum modulo 0xFFFF self.state += byte as u32; self.state = (self.state & consts::BASE) + (self.state >> consts::SIZE); } } + #[inline] + fn finish(&self) -> u64 { + self.state as u64 + } } @@ -53,7 +52,7 @@ impl digest::BlockInput for SysV { impl digest::Input for SysV { #[inline] fn process(&mut self, input: &[u8]) { - self.consume(input); + self.write(input); } } @@ -75,8 +74,8 @@ impl digest::FixedOutput for SysV { #[cfg(feature = "generic")] mod tests { + use core::hash::Hasher; use digest::Digest; - use digest::Input; use digest::FixedOutput; use generic_array::GenericArray; @@ -85,7 +84,7 @@ mod tests { let sysv = super::SysV::new(); let output: [u8; 2] = [0, 0]; - assert!(sysv.hash() == 0); + assert!(sysv.finish() == 0); assert!(sysv.fixed_result() == GenericArray::clone_from_slice(&output)); } @@ -94,9 +93,9 @@ mod tests { let mut sysv = super::SysV::new(); let output: [u8; 2] = [0, 'a' as u8]; - sysv.consume("a".as_bytes()); + sysv.write("a".as_bytes()); - assert!(sysv.hash() == 'a' as u16); + assert!(sysv.finish() == 'a' as u64); assert!(sysv.fixed_result() == GenericArray::clone_from_slice(&output)) } @@ -106,10 +105,10 @@ mod tests { let mut sysv2 = super::SysV::new(); let data = b"abcdef"; - sysv1.process(&data[..3]); - sysv1.process(&data[3..]); - sysv2.process(&data[..]); + sysv1.write(&data[..3]); + sysv1.write(&data[3..]); + sysv2.write(&data[..]); - assert!(sysv1.hash() == sysv2.hash()); + assert!(sysv1.finish() == sysv2.finish()); } }