Skip to content

Commit

Permalink
Implement Hasher trait for all sums
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Larralde committed Jul 20, 2017
1 parent 8e5f4dd commit 5b2d87b
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 134 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
52 changes: 26 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 13 additions & 21 deletions src/adler32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extern crate generic_array;
#[cfg(feature = "generic")]
extern crate digest;

use core::hash::Hasher;
use core::borrow::BorrowMut;


Expand All @@ -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) {
Expand All @@ -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
}
}


Expand All @@ -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);
}
}

Expand All @@ -92,7 +83,7 @@ impl digest::FixedOutput for Adler32 {
#[inline]
fn fixed_result(self) -> generic_array::GenericArray<u8, Self::OutputSize> {
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
}
}
Expand All @@ -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;
Expand All @@ -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));
}

Expand All @@ -127,6 +119,6 @@ mod tests {
adler1.process(&data[3..]);
adler2.process(&data[..]);

assert!(adler1.hash() == adler2.hash());
assert!(adler1.finish() == adler2.finish());
}
}
28 changes: 15 additions & 13 deletions src/bsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
}
}


Expand All @@ -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);
}
}

Expand All @@ -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;
Expand All @@ -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));
}

Expand All @@ -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))
}

Expand All @@ -110,6 +112,6 @@ mod tests {
bsd1.process(&data[3..]);
bsd2.process(&data[..]);

assert!(bsd1.hash() == bsd2.hash());
assert!(bsd1.finish() == bsd2.finish());
}
}
33 changes: 14 additions & 19 deletions src/crc32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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] = [
Expand Down Expand Up @@ -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;

Expand All @@ -119,6 +109,11 @@ impl CRC32 {

self.state = !crc;
}

#[inline]
fn finish(&self) -> u64 {
self.state as u64
}
}


Expand All @@ -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);
}
}

Expand All @@ -142,7 +137,7 @@ impl digest::FixedOutput for CRC32 {
#[inline]
fn fixed_result(self) -> generic_array::GenericArray<u8, Self::OutputSize> {
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
}
}
Expand All @@ -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;
Expand All @@ -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));
}

Expand All @@ -179,6 +174,6 @@ mod tests {
crc1.process(&data[3..]);
crc2.process(&data[..]);

assert!(crc1.hash() == crc2.hash());
assert!(crc1.finish() == crc2.finish());
}
}
Loading

0 comments on commit 5b2d87b

Please sign in to comment.