use super::Digest;
use super::block::{MdState, block_words_le, words_to_bytes_le};
const IV: [u32; 5] = [
0x6745_2301,
0xefcd_ab89,
0x98ba_dcfe,
0x1032_5476,
0xc3d2_e1f0,
];
const KL: [u32; 5] = [
0x0000_0000,
0x5a82_7999,
0x6ed9_eba1,
0x8f1b_bcdc,
0xa953_fd4e,
];
const KR: [u32; 5] = [
0x50a2_8be6,
0x5c4d_d124,
0x6d70_3ef3,
0x7a6d_76e9,
0x0000_0000,
];
#[rustfmt::skip]
const RL: [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, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13,
];
#[rustfmt::skip]
const RR: [usize; 80] = [
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11,
];
#[rustfmt::skip]
const SL: [u32; 80] = [
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, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6,
];
#[rustfmt::skip]
const SR: [u32; 80] = [
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
];
#[inline]
fn boolfn(round: usize, x: u32, y: u32, z: u32) -> u32 {
match round {
0 => x ^ y ^ z,
1 => (x & y) | (!x & z),
2 => (x | !y) ^ z,
3 => (x & z) | (y & !z),
_ => x ^ (y | !z),
}
}
fn compress(state: &mut [u32; 5], block: &[u8; 64]) {
let x = block_words_le(block);
let (mut al, mut bl, mut cl, mut dl, mut el) =
(state[0], state[1], state[2], state[3], state[4]);
let (mut ar, mut br, mut cr, mut dr, mut er) =
(state[0], state[1], state[2], state[3], state[4]);
for j in 0..80 {
let round = j / 16;
let tl = al
.wrapping_add(boolfn(round, bl, cl, dl))
.wrapping_add(x[RL[j]])
.wrapping_add(KL[round])
.rotate_left(SL[j])
.wrapping_add(el);
al = el;
el = dl;
dl = cl.rotate_left(10);
cl = bl;
bl = tl;
let tr = ar
.wrapping_add(boolfn(4 - round, br, cr, dr))
.wrapping_add(x[RR[j]])
.wrapping_add(KR[round])
.rotate_left(SR[j])
.wrapping_add(er);
ar = er;
er = dr;
dr = cr.rotate_left(10);
cr = br;
br = tr;
}
let t = state[1].wrapping_add(cl).wrapping_add(dr);
state[1] = state[2].wrapping_add(dl).wrapping_add(er);
state[2] = state[3].wrapping_add(el).wrapping_add(ar);
state[3] = state[4].wrapping_add(al).wrapping_add(br);
state[4] = state[0].wrapping_add(bl).wrapping_add(cr);
state[0] = t;
}
#[derive(Clone)]
pub struct Ripemd160 {
state: MdState<5>,
}
impl Digest for Ripemd160 {
type Output = [u8; 20];
type Block = [u8; 64];
const OUTPUT_LEN: usize = 20;
const BLOCK_LEN: usize = 64;
#[inline]
fn new() -> Self {
Ripemd160 {
state: MdState::new(IV, false, compress),
}
}
#[inline]
fn zeroed_block() -> [u8; 64] {
[0u8; 64]
}
#[inline]
fn zeroed_output() -> [u8; 20] {
[0u8; 20]
}
#[inline]
fn update(&mut self, data: &[u8]) {
self.state.update(data);
}
#[inline]
fn finalize(self) -> [u8; 20] {
words_to_bytes_le(&self.state.finalize())
}
#[inline]
fn zeroize(&mut self) {
self.state.zeroize();
}
}
#[inline]
pub fn ripemd160(data: &[u8]) -> [u8; 20] {
Ripemd160::digest(data)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::from_hex;
#[test]
fn reference_vectors() {
assert_eq!(
ripemd160(b""),
from_hex::<20>("9c1185a5c5e9fc54612808977ee8f548b2258d31")
);
assert_eq!(
ripemd160(b"abc"),
from_hex::<20>("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")
);
assert_eq!(
ripemd160(b"message digest"),
from_hex::<20>("5d0689ef49d2fae572b881b123a85ffa21595f36")
);
assert_eq!(
ripemd160(b"abcdefghijklmnopqrstuvwxyz"),
from_hex::<20>("f71c27109c692c1b56bbdceb5b9d2865b3708dbc")
);
assert_eq!(
ripemd160(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
from_hex::<20>("12a053384a9c0c88e405a06c27dcf49ada62eb2b")
);
}
#[test]
fn one_million_a() {
let mut h = Ripemd160::new();
let chunk = [b'a'; 1000];
for _ in 0..1000 {
h.update(&chunk);
}
assert_eq!(
h.finalize(),
from_hex::<20>("52783243c1697bdbe16d37f97f68f08325dc1528")
);
}
}