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

implementation of md6 in pure rust #636

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"md2",
"md4",
"md5",
"md6",
"ripemd",
"sha1",
"sha1-checked",
Expand Down
33 changes: 33 additions & 0 deletions md6/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "md6"
version = "0.1.0"
description = "MD6 hash function"
authors = ["RustCrypto Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
edition = "2021"
repository = "https://github.com/RustCrypto/hashes"
keywords = ["crypto", "md6", "hash", "digest"]
categories = ["cryptography", "no-std"]
rust-version = "1.81"

[lib]
name = "md6"
Copy link
Member

Choose a reason for hiding this comment

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

These two lines are not needed.


[dependencies]
digest = "=0.11.0-pre.9"

[dev-dependencies]
digest = { version = "=0.11.0-pre.9", features = ["dev"] }
hex-literal = "0.4"
base16ct = { version = "0.2", features = ["alloc"] }

[features]
default = ["oid", "std"]
std = ["digest/std"]
oid = ["digest/oid"]
zeroize = ["digest/zeroize"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Copy link
Member

Choose a reason for hiding this comment

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

docsrs flag is passed by default during docs.rs builds and no longer needed. I will remove it from other crates in a separate PR.

83 changes: 83 additions & 0 deletions md6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# RustCrypto: MD6

Pure Rust implementation of the MD6 hash function.

## Example

### Fixed output size

```rust
use md6::Md6_256;
use digest::Digest;
use hex_literal::hex;

// create a Md6_256 object
let mut hasher = Md6_256::new();

// write input message
hasher.update(b"hello world");

// read hash digest and consume hasher
let hash = hasher.finalize();
assert_eq!(hash.to_vec(), hex!(
"9ae602639631cc2c60adaa7a952aae8756141f31a7e6a9b76adc1de121db2230"
));
```

Also, see the [examples section] in the RustCrypto/hashes readme.

### Variable output size

This implementation supports run and compile time variable sizes.

Output size set at run time:
```rust
use md6::Md6Var;
use digest::{Update, VariableOutput};
use hex_literal::hex;

let mut hasher = Md6Var::new(12).unwrap();
hasher.update(b"hello rust");
let mut buf = [0u8; 12];
hasher.finalize_variable(&mut buf).unwrap();
assert_eq!(buf, hex!("9c5b8d9744898ec981bcc573"));
```

Output size set at compile time:
```rust
use md6::Md6;
use digest::{Digest, consts::U20};
use hex_literal::hex;

type Md6_160 = Md6<U20>;

let mut hasher = Md6_160::new();
hasher.update(b"hello rust");
let res = hasher.finalize();
assert_eq!(res, hex!("576d736a93a555a1c868973cfdd2d21838a26623"));
```

## Minimum Supported Rust Version

Rust **1.81** or higher.

Minimum supported Rust version can be changed in the future, but it will be
done with a minor version bump.

## SemVer Policy

- All on-by-default features of this library are covered by SemVer
- MSRV is considered exempt from SemVer as noted above

## License

The crate is licensed under either of:

* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)

## Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
54 changes: 54 additions & 0 deletions md6/benches/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![feature(test)]
extern crate test;

use digest::bench_update;
use md6::{Md6_128, Md6_224, Md6_256, Md6_384, Md6_512, Md6_64};
use test::Bencher;

bench_update!(
Md6_64::default();
md6_64_10 10;
md6_64_100 100;
md6_64_1000 1000;
md6_64_10000 10000;
);

bench_update!(
Md6_128::default();
md6_128_10 10;
md6_128_100 100;
md6_128_1000 1000;
md6_128_10000 10000;
);

bench_update!(
Md6_224::default();
md6_224_10 10;
md6_224_100 100;
md6_224_1000 1000;
md6_224_10000 10000;
);

bench_update!(
Md6_256::default();
md6_256_10 10;
md6_256_100 100;
md6_256_1000 1000;
md6_256_10000 10000;
);

bench_update!(
Md6_384::default();
md6_384_10 10;
md6_384_100 100;
md6_384_1000 1000;
md6_384_10000 10000;
);

bench_update!(
Md6_512::default();
md6_512_10 10;
md6_512_100 100;
md6_512_1000 1000;
md6_512_10000 10000;
);
194 changes: 194 additions & 0 deletions md6/src/compress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use crate::consts::*;

const W: usize = MD6_W; // number of bits in a word (64)
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 simply rename these constants in the consts module.

const C: usize = MD6_C; // size of compression output in words (16)
const N: usize = MD6_N; // size of compression input block in words (89)
const Q: usize = MD6_Q; // Q words in a compression block (>= 0) (15)
const K: usize = MD6_K; // key words per compression block (>= 0) (8)
const U: usize = MD6_U; // words for unique node ID (0 or 64/w)
const V: usize = MD6_V; // words for control word (0 or 64/w)
const B: usize = MD6_B; // data words per compression block (> 0) (64)

const T0: usize = 17; // index for linear feedback
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 worth to move these constants to the consts module.

const T1: usize = 18; // index for first input to first and
const T2: usize = 21; // index for second input to first and
const T3: usize = 31; // index for first input to second and
const T4: usize = 67; // index for second input to second and
const T5: usize = 89; // last tap

macro_rules! call_loop_bodies {
($w: ident, $s: expr, $i: expr) => {
if $w == 64 {
loop_body!(10, 11, 0, $s, $i);
loop_body!(5, 24, 1, $s, $i);
loop_body!(13, 9, 2, $s, $i);
loop_body!(10, 16, 3, $s, $i);
loop_body!(11, 15, 4, $s, $i);
loop_body!(12, 9, 5, $s, $i);
loop_body!(2, 27, 6, $s, $i);
loop_body!(7, 15, 7, $s, $i);
loop_body!(14, 6, 8, $s, $i);
loop_body!(15, 2, 9, $s, $i);
loop_body!(7, 29, 10, $s, $i);
loop_body!(13, 8, 11, $s, $i);
loop_body!(11, 15, 12, $s, $i);
loop_body!(7, 5, 13, $s, $i);
loop_body!(6, 31, 14, $s, $i);
loop_body!(12, 9, 15, $s, $i);
} else if $w == 32 {
loop_body!(5, 4, 0, $s, $i);
loop_body!(3, 7, 1, $s, $i);
loop_body!(6, 7, 2, $s, $i);
loop_body!(5, 9, 3, $s, $i);
loop_body!(4, 13, 4, $s, $i);
loop_body!(6, 8, 5, $s, $i);
loop_body!(7, 4, 6, $s, $i);
loop_body!(3, 14, 7, $s, $i);
loop_body!(5, 7, 8, $s, $i);
loop_body!(6, 4, 9, $s, $i);
loop_body!(5, 8, 10, $s, $i);
loop_body!(5, 11, 11, $s, $i);
loop_body!(4, 5, 12, $s, $i);
loop_body!(6, 8, 13, $s, $i);
loop_body!(7, 2, 14, $s, $i);
loop_body!(5, 11, 15, $s, $i);
} else if $w == 16 {
loop_body!(5, 6, 0, $s, $i);
loop_body!(4, 7, 1, $s, $i);
loop_body!(3, 2, 2, $s, $i);
loop_body!(5, 4, 3, $s, $i);
loop_body!(7, 2, 4, $s, $i);
loop_body!(5, 6, 5, $s, $i);
loop_body!(5, 3, 6, $s, $i);
loop_body!(2, 7, 7, $s, $i);
loop_body!(4, 5, 8, $s, $i);
loop_body!(3, 7, 9, $s, $i);
loop_body!(4, 6, 10, $s, $i);
loop_body!(3, 5, 11, $s, $i);
loop_body!(4, 5, 12, $s, $i);
loop_body!(7, 6, 13, $s, $i);
loop_body!(7, 4, 14, $s, $i);
loop_body!(2, 3, 15, $s, $i);
} else if $w == 8 {
loop_body!(3, 2, 0, $s, $i);
loop_body!(3, 4, 1, $s, $i);
loop_body!(3, 2, 2, $s, $i);
loop_body!(4, 3, 3, $s, $i);
loop_body!(3, 2, 4, $s, $i);
loop_body!(3, 2, 5, $s, $i);
loop_body!(3, 2, 6, $s, $i);
loop_body!(3, 4, 7, $s, $i);
loop_body!(2, 3, 8, $s, $i);
loop_body!(2, 3, 9, $s, $i);
loop_body!(3, 2, 10, $s, $i);
loop_body!(2, 3, 11, $s, $i);
loop_body!(2, 3, 12, $s, $i);
loop_body!(3, 4, 13, $s, $i);
loop_body!(2, 3, 14, $s, $i);
loop_body!(3, 4, 15, $s, $i);
}
};
}

fn get_s_constants(ws: usize) -> (Md6Word, Md6Word) {
match ws {
64 => (0x0123456789abcdef, 0x7311c2812425cfa0),
32 => (0x01234567, 0x7311c281),
16 => (0x01234, 0x7311),
8 => (0x01, 0x73),
_ => panic!("bad w"),
}
}

fn main_compression_loop(a: &mut [Md6Word], r: usize) {
macro_rules! loop_body {
($rs: expr, $ls: expr, $step: expr, $s: expr, $i: expr) => {
let mut x = $s; // feedback constant
x ^= a[$i + $step - T5]; // end-around feedback
x ^= a[$i + $step - T0]; // linear feedback
x ^= (a[$i + $step - T1] & a[$i + $step - T2]); // first quadratic term
x ^= (a[$i + $step - T3] & a[$i + $step - T4]); // second quadratic term
x ^= x >> $rs; // right shift
a[$i + $step] = x ^ (x << $ls); // left shift
};
}

// Get the initial values for `s` and `smask` based on the width `w`.
let (mut s, smask) = get_s_constants(W);

let mut i = N;
let mut j = 0;

while j < r * C {
// Call the loop bodies based on the value of `w`.
// This will perform the main computation for each step in the compression loop.
call_loop_bodies!(W, s, i);

// Advance round constant s to the next round constant.
s = (s << 1) ^ (s >> (W - 1)) ^ (s & smask);
i += 16;
j += C;
}
}

pub fn compress(c: &mut [Md6Word], n: &mut [Md6Word], r: usize, a: &mut [Md6Word]) {
// check that the input is sensible
assert!(!n.is_empty());
assert!(!n.is_empty());
assert!(r <= MD6_MAX_R);
assert!(!a.is_empty());

a[..n.len()].copy_from_slice(n); // copy n to front of a

main_compression_loop(a, r); // do the main computation

c.copy_from_slice(&a[((r - 1) * C + N)..((r - 1) * C + N + C)]); // output into c
}

pub fn make_control_word(
r: usize,
l: usize,
z: usize,
p: usize,
keylen: usize,
d: usize,
) -> Md6ControlWord {
(0 as Md6ControlWord) << 60 // reserved width 4 bits
| (r as Md6ControlWord) << 48 // r width 12 bits
| (l as Md6ControlWord) << 40 // L width 8 bits
| (z as Md6ControlWord) << 36 // z width 4 bits
| (p as Md6ControlWord) << 20 // p width 16 bits
| (keylen as Md6ControlWord) << 12 // keylen width 8 bits
| (d as Md6ControlWord) // d width 12 bits
}

pub fn make_node_id(ell: usize, i: Md6Word) -> Md6NodeID {
(ell as Md6NodeID) << 56 | i // ell width 8 bits, i width 56 bits
}

pub fn pack(
n: &mut [Md6Word],
q: &[Md6Word],
k: [Md6Word; K],
b: [Md6Word; 64],
u: Md6NodeID,
v: Md6ControlWord,
) {
let mut ni = 0;

n[ni..ni + Q].copy_from_slice(&q[..Q]); // q: q in words 0--14
ni += Q;

n[ni..ni + K].copy_from_slice(&k[..K]); // k: key in words 15--22
ni += K;

// u: unique node ID in 23
n[ni] = u;
ni += U;

// v: control word in 24
n[ni] = v;
ni += V;

n[ni..ni + B].copy_from_slice(&b[..B]); // b: data words 25--88
}
Loading
Loading