Skip to content

Commit

Permalink
all ripemd versions working
Browse files Browse the repository at this point in the history
  • Loading branch information
SymmetricChaos committed Aug 27, 2024
1 parent fb0c121 commit 10e8734
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 81 deletions.
8 changes: 6 additions & 2 deletions hashers/src/ids/hasher_descriptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,13 @@
"Authors": "Bertoni et al.",
"Publication": "2006"
},
"SHA-0": "SHA-0 (NSA 1993) was the original cryptographic hash function designed by the US National Security Agency, first specified is 1992. It produces a 160 bit (20 byte) hash. In 1995, shortly after being official published, it was withdrawn and replaced with SHA-1, which differs only in the addition of a one bit rotation during the expansion step of the compression function, to correct an unspecified security flaw. Practical attacks on SHA-0 have been publicly known since 2004.",
"RIPEMD": {
"Description": "RIPEMD is a family of five hash functions. The original RIPEMD is 128-bit hash function with three rounds which was found to have a low security margin. Four improved versions were proposed. RIPEMD-128 and the extended RIPEMD-256 have four rounds. RIPEMD-160 and the extended RIPEMD-320 have five rounds. The extended versions do not claim any increased security.",
"Authors": "Dobbertin, Bosselaers, and Preneel",
"Publication": "1996"
},
"SHA-1": {
"Description": "SHA-1 is a cryptographic hash function designed by the US National Security Agency, first released is 1995. It produces a 160 bit (20 byte) hash. Practical attacks on SHA-1 have been publicly known since 2017",
"Description": "SHA-1 is a cryptographic hash function designed by the US National Security Agency, first released is 1995. It produces a 160 bit (20 byte) hash. Practical attacks on SHA-1 have been publicly known since 2017. It it based on the similar SHA-0, released in 1993, which different only in lacking a one bit rotation during the expansion step. Practical attacks on SHA-0 have been publicly known since 2004.",
"Authors": "NSA",
"Publication": "1995"
},
Expand Down
2 changes: 2 additions & 0 deletions hashers/src/ripemd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pub mod ripemd;
pub mod ripemd128;
pub mod ripemd160;
pub mod ripemd256;
pub mod ripemd320;
pub mod ripemd0;

pub const PERM: [usize; 80] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5,
Expand Down
69 changes: 69 additions & 0 deletions hashers/src/ripemd/ripemd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::fmt::Display;

use strum::EnumIter;
use utils::byte_formatting::ByteFormat;

use crate::traits::ClassicHasher;

use super::{
ripemd0::RipeMd0, ripemd128::RipeMd128, ripemd160::RipeMd160, ripemd256::RipeMd256,
ripemd320::RipeMd320,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter)]
pub enum RipeMdVariant {
Md0,
Md128,
Md160,
Md256,
Md320,
}

impl Display for RipeMdVariant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RipeMdVariant::Md0 => write!(f, "RIPEMD (original)"),
RipeMdVariant::Md128 => write!(f, "RIPEMD-128"),
RipeMdVariant::Md160 => write!(f, "RIPEMD-160"),
RipeMdVariant::Md256 => write!(f, "RIPEMD-256"),
RipeMdVariant::Md320 => write!(f, "RIPEMD-320"),
}
}
}

impl RipeMdVariant {
pub fn hasher(&self) -> Box<dyn ClassicHasher> {
match self {
RipeMdVariant::Md0 => Box::new(RipeMd0::default()),
RipeMdVariant::Md128 => Box::new(RipeMd128::default()),
RipeMdVariant::Md160 => Box::new(RipeMd160::default()),
RipeMdVariant::Md256 => Box::new(RipeMd256::default()),
RipeMdVariant::Md320 => Box::new(RipeMd320::default()),
}
}
}

#[derive(Clone)]
pub struct RipeMd {
pub input_format: ByteFormat,
pub output_format: ByteFormat,
pub variant: RipeMdVariant,
}

impl Default for RipeMd {
fn default() -> Self {
Self {
input_format: ByteFormat::Utf8,
output_format: ByteFormat::Hex,
variant: RipeMdVariant::Md160,
}
}
}

impl ClassicHasher for RipeMd {
fn hash(&self, bytes: &[u8]) -> Vec<u8> {
self.variant.hasher().hash(bytes)
}

crate::hash_bytes_from_string! {}
}
122 changes: 122 additions & 0 deletions hashers/src/ripemd/ripemd0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use crate::traits::ClassicHasher;
use utils::{byte_formatting::ByteFormat, padding::md_strengthening_64_le};

// Similiar but not identical to the strengthened versions
pub const PERM: [usize; 48] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5,
14, 2, 11, 8, 3, 10, 2, 4, 9, 15, 8, 1, 14, 7, 0, 6, 11, 13, 5, 12,
];

// Similiar but not identical to the strengthened versions
pub const ROL: [u32; 48] = [
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15,
9, 7, 11, 13, 12, 11, 13, 14, 7, 14, 9, 13, 15, 6, 8, 13, 6, 12, 5, 7, 5,
];

// Selectable boolean function
fn f(j: usize, x: u32, y: u32, z: u32) -> u32 {
match j / 16 {
0 => (x & y) | (!x & z),
1 => (x & y) | (x & z) | (y & z),
2 => x ^ y ^ z,
_ => unreachable!(),
}
}

fn ff(j: usize, s: &mut [u32; 4], block: [u32; 16], k: u32) {
s[0] = (s[0]
.wrapping_add(f(j, s[1], s[2], s[3]))
.wrapping_add(block[PERM[j]].wrapping_add(k)))
.rotate_left(ROL[j]);
s.rotate_right(1);
}

#[derive(Clone)]
pub struct RipeMd0 {
pub input_format: ByteFormat,
pub output_format: ByteFormat,
}

impl Default for RipeMd0 {
fn default() -> Self {
Self {
input_format: ByteFormat::Utf8,
output_format: ByteFormat::Hex,
}
}
}

impl RipeMd0 {
pub const K: [u32; 4] = [0x50a28be6, 0x5a827999, 0x6ed9eba1, 0x5c4dd124];

pub fn input(mut self, input: ByteFormat) -> Self {
self.input_format = input;
self
}

pub fn output(mut self, output: ByteFormat) -> Self {
self.output_format = output;
self
}

pub fn compress(state: &mut [u32; 4], block: [u32; 16]) {
let mut l = state.clone();
let mut r = state.clone();

for j in 0..16 {
ff(j, &mut l, block, 0);
ff(j, &mut r, block, Self::K[0]);
}
for j in 0..16 {
ff(j + 16, &mut l, block, Self::K[1]);
ff(j + 16, &mut r, block, 0);
}
for j in 0..16 {
ff(j + 32, &mut l, block, Self::K[2]);
ff(j + 32, &mut r, block, Self::K[3]);
}

let t = state[1].wrapping_add(l[2]).wrapping_add(r[3]);
state[1] = state[2].wrapping_add(l[3]).wrapping_add(r[0]);
state[2] = state[3].wrapping_add(l[0]).wrapping_add(r[1]);
state[3] = state[0].wrapping_add(l[1]).wrapping_add(r[2]);
state[0] = t;
}
}

impl ClassicHasher for RipeMd0 {
fn hash(&self, bytes: &[u8]) -> Vec<u8> {
let mut input = bytes.to_vec();

md_strengthening_64_le(&mut input, 64);

let mut state: [u32; 4] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];

for chunk in input.chunks_exact(64) {
let mut block = [0u32; 16];
for (elem, b) in block.iter_mut().zip(chunk.chunks_exact(4)) {
*elem = u32::from_le_bytes(b.try_into().unwrap());
}
Self::compress(&mut state, block)
}

let mut out = Vec::with_capacity(16);
for word in state {
out.extend(word.to_le_bytes())
}
out
}

crate::hash_bytes_from_string! {}
}

crate::basic_hash_tests!(
RipeMd0::default(), test_0_1, "",
"9f73aa9b372a9dacfb86a6108852e2d9";
RipeMd0::default(), test_0_2, "a",
"486f74f790bc95ef7963cd2382b4bbc9";
RipeMd0::default(), test_0_3, "abc",
"3f14bad4c2f9b0ea805e5485d3d6882d";
RipeMd0::default(), test_0_4, "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
"dfd6b45f60fe79bbbde87c6bfc6580a5";
);
4 changes: 2 additions & 2 deletions hashers/src/ripemd/ripemd128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl RipeMd128 {
self
}

fn left_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
pub(super) fn left_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(j, s[1], s[2], s[3]))
.wrapping_add(block[PERM[j]])
Expand All @@ -45,7 +45,7 @@ impl RipeMd128 {
s[1] = t;
}

fn right_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
pub(super) fn right_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(63 - j, s[1], s[2], s[3]))
.wrapping_add(block[PERM_PRIME[j]])
Expand Down
4 changes: 2 additions & 2 deletions hashers/src/ripemd/ripemd160.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl RipeMd160 {
self
}

fn left_chain(j: usize, s: &mut [u32; 5], block: [u32; 16]) {
pub(super) fn left_chain(j: usize, s: &mut [u32; 5], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(j, s[1], s[2], s[3]))
.wrapping_add(block[PERM[j]])
Expand All @@ -47,7 +47,7 @@ impl RipeMd160 {
s[1] = t;
}

fn right_chain(j: usize, s: &mut [u32; 5], block: [u32; 16]) {
pub(super) fn right_chain(j: usize, s: &mut [u32; 5], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(79 - j, s[1], s[2], s[3]))
.wrapping_add(block[PERM_PRIME[j]])
Expand Down
55 changes: 20 additions & 35 deletions hashers/src/ripemd/ripemd256.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::ripemd128::RipeMd128;
use crate::traits::ClassicHasher;
use utils::{byte_formatting::ByteFormat, padding::md_strengthening_64_le};

use super::{f, PERM, PERM_PRIME, ROL, ROL_PRIME};

#[derive(Clone)]
pub struct RipeMd256 {
pub input_format: ByteFormat,
Expand All @@ -19,10 +18,6 @@ impl Default for RipeMd256 {
}

impl RipeMd256 {
pub const K: [u32; 4] = [0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc];

pub const K_PRIME: [u32; 4] = [0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x00000000];

pub fn input(mut self, input: ByteFormat) -> Self {
self.input_format = input;
self
Expand All @@ -33,39 +28,29 @@ impl RipeMd256 {
self
}

fn left_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(j, s[1], s[2], s[3]))
.wrapping_add(block[PERM[j]])
.wrapping_add(Self::K[j / 16]))
.rotate_left(ROL[j]);
s[0] = s[3];
s[3] = s[2];
s[2] = s[1];
s[1] = t;
}

fn right_chain(j: usize, s: &mut [u32; 4], block: [u32; 16]) {
let t = (s[0]
.wrapping_add(f(63 - j, s[1], s[2], s[3]))
.wrapping_add(block[PERM_PRIME[j]])
.wrapping_add(Self::K_PRIME[j / 16]))
.rotate_left(ROL_PRIME[j]);
s[0] = s[3];
s[3] = s[2];
s[2] = s[1];
s[1] = t;
}

pub fn compress(state_l: &mut [u32; 4], state_r: &mut [u32; 4], block: [u32; 16]) {
let mut l = state_l.clone();
let mut r = state_r.clone();
for i in 0..4 {
for j in 0..16 {
Self::left_chain(16 * i + j, &mut l, block);
Self::right_chain(16 * i + j, &mut r, block);
for j in 0..64 {
// Exact same round functions as RIPEMD-128
RipeMd128::left_chain(j, &mut l, block);
RipeMd128::right_chain(j, &mut r, block);
// While in RIPEMD-128 the l and r arrays and mixed together
// at the end of the compression function that is not possible
// here since both are needed for output. Instead at the end of
// each 16 step round a word is swapped between them.
if j == 15 {
std::mem::swap(&mut l[0], &mut r[0])
}
if j == 31 {
std::mem::swap(&mut l[1], &mut r[1])
}
if j == 47 {
std::mem::swap(&mut l[2], &mut r[2])
}
if j == 63 {
std::mem::swap(&mut l[3], &mut r[3])
}
std::mem::swap(&mut l[i], &mut r[i])
}
for i in 0..4 {
state_l[i] = state_l[i].wrapping_add(l[i]);
Expand Down
Loading

0 comments on commit 10e8734

Please sign in to comment.