Skip to content
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

Implemented Grostl #10

Merged
merged 26 commits into from
Jan 22, 2017
Merged

Implemented Grostl #10

merged 26 commits into from
Jan 22, 2017

Conversation

gsingh93
Copy link
Contributor

@gsingh93 gsingh93 commented Jan 17, 2017

This PR contains an implementation of Grostl. I mainly used this document as a reference for implementation. This page contains a guide for different optimizations one can do in an implementation, as well as many optimized reference implementations, but I chose to implement none of these. The goal of my implementation is that it should be correct, readable, and well tested. This is because none of the other implementations are readable or well-tested, which made using them as a reference difficult. After this is merged, we can work on optimizations, as we'd have a nice reference implementation to test against.

One way Grostl differs from most other hash functions is that it allows you to specify a variable digest size between 1 and 64 bytes. However the block size is different between digests of size 1 to 32 and 33 to 64. In order to prevent people from using the wrong block size, I've added GrostlSmall and GrostlBig which should prevent you from making this mistake when you specify your custom OutputSize. I've also provided type aliases for the common digest sizes, 224, 256, 384, and 512.

While the code is done, two minor things I plan on adding are more comments/documentation, more/better tests (especially around edge cases like padding), and cleaning up some of the assertions. It should be fine for you to start reviewing now, as these are minor changes.

pub type Grostl224 = GrostlSmall<U28>;
pub type Grostl256 = GrostlSmall<U32>;
pub type Grostl384 = GrostlSmall<U48>;
pub type Grostl512 = GrostlSmall<U64>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. The fact that GrostlSmall compiles with a size greater than U32 is a problem...

Copy link
Member

@newpavlov newpavlov Jan 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work!

Unfortunately it seems that according to E0122 "trait bounds are not (yet) enforced in type definitions" and because of the bug I've missed it. (also you have typo in GrostlSmall which got through because of that, you needed to use Cmp<U33>)

So you'll need to write structs explicitly like this:

#[derive(Default)]
pub struct GrostlSmall<OutputSize>
   where OutputSize: ArrayLength<u8> + Cmp<U33>,
         Compare<OutputSize, U33>: Same<Less>
{
   grostl: Grostl<OutputSize, U64>,
}

impl<OutputSize> Digest for GrostlSmall<OutputSize> {
   type OutputSize = OutputSize;
   type BlockSize = U64;

   fn input(&mut self, input: &[u8]) {
       self.grostl.process(input);
   }

   fn result(mut self) -> GenericArray<u8, Self::OutputSize> {
       self.grostl.finalize()
   }
}

It's probably not worth to implement Digest for Grostl in this situation.

I will review your code shortly.

Copy link
Member

@newpavlov newpavlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a cursory review. I haven't looked deep into implementation yet, but it looks good.

Though formatting differs from used in other crates. But it's my fault, as I haven't got time to make rustfmt config. (personally I am not fond of multiline approach of rustfmt)

Also, please add LICENSE files as in other crates.

@@ -0,0 +1,18 @@
[package]
name = "grostl"
version = "0.3.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to start from 0.1.0 as we don't require version synchronization across crates.

authors = ["The Rust-Crypto Project Developers"]
license = "MIT/Apache-2.0"
description = "Grostl hash function"
documentation = "https://docs.rs/md5"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong link to docs.

use generic_array::typenum::{Quot, U8};
use matrix::Matrix;

const SBOX: [u8; 256] = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to move constants to the separate module, in other crates I've used consts.rs for this purpose.

const SHIFTS_Q_WIDE: [u8; 8] = [1, 3, 5, 11, 0, 2, 4, 6];

pub struct Grostl<OutputSize, BlockSize: ArrayLength<u8>>
where BlockSize::ArrayType: Copy
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add #[derive(Copy, Clone)] to Grostl and GrostlState. You'll need to add several BlockSize::ArrayType: Copy trait bounds for that to work.

let block_bytes = BlockSize::to_usize();
if block_bytes == 64 {
false
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use match statement with unreachable! macro?

#[cfg(test)]
mod test {
use super::{xor_generic_array, C_P, C_Q, Grostl, SHIFTS_P};
use generic_array::typenum::{U32, U64};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to move this test module to the tests.rs and use:

#[cfg(test)]
mod tests

In the lib.rs.

Copy link
Member

@newpavlov newpavlov Jan 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the second thought it will require to make tested methods public, they still will not be accessible to the crate user as grostl field will be private, but perhaps it's better to leave it as is. Your choice.


struct GrostlState<OutputSize, BlockSize: ArrayLength<u8>> {
state: GenericArray<u8, BlockSize>,
rounds: u8,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also can impl Default for GrostlState. This will allow you to simplify code and tests a bit and you will be able to simply use #[derive(Default)] on Grostl.

extern crate grostl;

use crypto_tests::hash::{Test, main_test};
use generic_array::typenum::{U28, U32, U48, U64};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use type aliases instead of using GrostlSmall and GrostlBig directly. In my opinion it will look a bit nicer.

@newpavlov
Copy link
Member

newpavlov commented Jan 18, 2017

By the way, correct name for Grøstl is Groestl, not Grostl. So it's better to rename crate. :)

Adding "grostl" to kewords should help with searchability.

@gsingh93
Copy link
Contributor Author

gsingh93 commented Jan 22, 2017

Ok, I believe I've addressed all of your comments. The crate and all structs should be renamed, the Small/Big issue should be fixed, and the other minor code changes were made. I decided to leave the tests in the same mod, as those were unit tests which need access to the private functions. The tests in tests are integration tests and only use the public API.

I still need to add some tests to ensure the padding is correct, I haven't convinced myself that it is. I'm also planning on adding a benchmark, as I see there's one in the utils crate.

Also, what names do I put for the copyright in the LICENSE file? My own?

Copy link
Member

@newpavlov newpavlov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several minor comments.

@@ -0,0 +1,27 @@
Copyright (c) 2006-2009 Graydon Hoare
Copyright (c) 2009-2013 Mozilla Foundation
Copyright (c) 2016 Artyom Pavlov
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's your work, so it's better to attribute yourself.

fn result(mut self) -> GenericArray<u8, Self::OutputSize> {
self.buffer.next(1)[0] = 128;
if self.buffer.remaining() >= 8 {
let block_bytes = self.block_bytes();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use standard_padding method from digest-buffer to reduce amount of code.


impl<OutputSize, BlockSize> Digest for Groestl<OutputSize, BlockSize>
where OutputSize: ArrayLength<u8>,
BlockSize: ArrayLength<u8> + Div<U8>,
Copy link
Member

@newpavlov newpavlov Jan 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it's worth to implement Digest for Groestl. You can move input and result to struct's impl. It'll reduce amount of code a bit.

#[test]
fn test_shift_bytes() {
let g: Groestl<U32, U64> = Groestl::default();
let s = g.state;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do not use g anywhere after that. Maybe just write let s = GroestlState::<U32, U64>::default();? The same goes for other tests as well.

48, 57, 2, 11, 20, 29, 38, 47,
56, 1, 10, 19, 28, 37, 46, 55,
];
assert_eq!(&*block, &expected[..]);
Copy link
Member

@newpavlov newpavlov Jan 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write it as assert_eq!(&block[..], &expected[..]);, which will be a bit clearer in my opinion. The same goes for other tests. I'll probably create later PR for generic-array with implementation of PartialEq<[T]>.

//! let mut hasher = GroestlBig::<U52>::new();
//! hasher.input(b"my message");
//! let result = hasher.result();
//!}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary }.

//! ```
//!
//! ```rust,ignore
//! use groestl::{Digest, Groestl256};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be GroestlSmall and GroestlBig, not Groestl256.

mod matrix;

#[derive(Default)]
pub struct GroestlSmall<OutputSize>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to derive Copy and Clone.

}

#[derive(Default)]
pub struct GroestlBig<OutputSize>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy and Clone.

@gsingh93
Copy link
Contributor Author

Ok, I made those changes and I added some benchmarks. Since I'm using the standard_padding function now, I'm going to assume everything is fine and I don't need to write any extra tests, so I think this should be good to go.

@newpavlov
Copy link
Member

Thank you for you work!

Unfortunately performance is a bit lower than I expected (slightly less than 1 MB/s), but I will leave it for future improvements, I'll add your links to the related issue.

I will publish this crate in the next few days.

@newpavlov newpavlov merged commit 86e3886 into RustCrypto:master Jan 22, 2017
@newpavlov newpavlov mentioned this pull request Jan 22, 2017
@newpavlov newpavlov mentioned this pull request Feb 2, 2017
18 tasks
linkmauve pushed a commit to linkmauve/hashes that referenced this pull request Jan 4, 2020
Add AArch64 sha1 and sha2 extension support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants