1use super::Digest;
8
9const IV: [u32; 5] = [
11 0x6745_2301,
12 0xefcd_ab89,
13 0x98ba_dcfe,
14 0x1032_5476,
15 0xc3d2_e1f0,
16];
17
18const RL: [usize; 80] = [
20 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,
21 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,
22 13, 3, 7, 15, 14, 5, 6, 2, 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13,
23];
24
25const RR: [usize; 80] = [
27 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,
28 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,
29 12, 2, 13, 9, 7, 10, 14, 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11,
30];
31
32const SL: [u32; 80] = [
34 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,
35 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,
36 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,
37];
38
39const SR: [u32; 80] = [
41 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,
42 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,
43 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,
44];
45
46#[inline]
47fn f(j: usize, x: u32, y: u32, z: u32) -> u32 {
48 match j {
50 0..=15 => x ^ y ^ z,
51 16..=31 => (x & y) | (!x & z),
52 32..=47 => (x | !y) ^ z,
53 48..=63 => (x & z) | (y & !z),
54 _ => x ^ (y | !z),
55 }
56}
57
58#[inline]
59fn k_left(j: usize) -> u32 {
60 match j {
62 0..=15 => 0x0000_0000,
63 16..=31 => 0x5a82_7999,
64 32..=47 => 0x6ed9_eba1,
65 48..=63 => 0x8f1b_bcdc,
66 _ => 0xa953_fd4e,
67 }
68}
69
70#[inline]
71fn k_right(j: usize) -> u32 {
72 match j {
74 0..=15 => 0x50a2_8be6,
75 16..=31 => 0x5c4d_d124,
76 32..=47 => 0x6d70_3ef3,
77 48..=63 => 0x7a6d_76e9,
78 _ => 0x0000_0000,
79 }
80}
81
82#[inline]
83fn compress(state: &mut [u32; 5], block: &[u8; 64]) {
84 let mut words = [0u32; 16];
85 for (i, chunk) in block.chunks_exact(4).enumerate() {
86 words[i] = u32::from_le_bytes(chunk.try_into().expect("4-byte chunk"));
87 }
88
89 let mut al = state[0];
92 let mut bl = state[1];
93 let mut cl = state[2];
94 let mut dl = state[3];
95 let mut el = state[4];
96
97 let mut ar = state[0];
98 let mut br = state[1];
99 let mut cr = state[2];
100 let mut dr = state[3];
101 let mut er = state[4];
102
103 for j in 0..80 {
104 let tl = al
105 .wrapping_add(f(j, bl, cl, dl))
106 .wrapping_add(words[RL[j]])
107 .wrapping_add(k_left(j))
108 .rotate_left(SL[j])
109 .wrapping_add(el);
110 al = el;
111 el = dl;
112 dl = cl.rotate_left(10);
113 cl = bl;
114 bl = tl;
115
116 let tr = ar
117 .wrapping_add(f(79 - j, br, cr, dr))
118 .wrapping_add(words[RR[j]])
119 .wrapping_add(k_right(j))
120 .rotate_left(SR[j])
121 .wrapping_add(er);
122 ar = er;
123 er = dr;
124 dr = cr.rotate_left(10);
125 cr = br;
126 br = tr;
127 }
128
129 let t = state[1].wrapping_add(cl).wrapping_add(dr);
132 state[1] = state[2].wrapping_add(dl).wrapping_add(er);
133 state[2] = state[3].wrapping_add(el).wrapping_add(ar);
134 state[3] = state[4].wrapping_add(al).wrapping_add(br);
135 state[4] = state[0].wrapping_add(bl).wrapping_add(cr);
136 state[0] = t;
137}
138
139#[derive(Clone)]
140pub struct Ripemd160 {
141 state: [u32; 5],
142 block: [u8; 64],
143 pos: usize,
144 bit_len: u64,
145}
146
147impl Default for Ripemd160 {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153impl Ripemd160 {
154 pub const BLOCK_LEN: usize = 64;
155 pub const OUTPUT_LEN: usize = 20;
156
157 #[must_use]
158 pub fn new() -> Self {
159 Self {
160 state: IV,
161 block: [0u8; 64],
162 pos: 0,
163 bit_len: 0,
164 }
165 }
166
167 pub fn update(&mut self, mut data: &[u8]) {
168 while !data.is_empty() {
169 let take = (64 - self.pos).min(data.len());
170 self.block[self.pos..self.pos + take].copy_from_slice(&data[..take]);
171 self.pos += take;
172 data = &data[take..];
173
174 if self.pos == 64 {
175 compress(&mut self.state, &self.block);
176 self.block = [0u8; 64];
177 self.pos = 0;
178 self.bit_len = self.bit_len.wrapping_add(512);
179 }
180 }
181 }
182
183 #[must_use]
184 pub fn finalize(mut self) -> [u8; 20] {
185 self.bit_len = self.bit_len.wrapping_add((self.pos as u64) * 8);
186
187 self.block[self.pos] = 0x80;
188 self.pos += 1;
189
190 if self.pos > 56 {
191 self.block[self.pos..].fill(0);
192 compress(&mut self.state, &self.block);
193 self.block = [0u8; 64];
194 self.pos = 0;
195 }
196
197 self.block[self.pos..56].fill(0);
198 self.block[56..].copy_from_slice(&self.bit_len.to_le_bytes());
199 compress(&mut self.state, &self.block);
200
201 let mut out = [0u8; 20];
202 for (chunk, word) in out.chunks_exact_mut(4).zip(self.state.iter()) {
203 chunk.copy_from_slice(&word.to_le_bytes());
204 }
205 out
206 }
207
208 #[must_use]
209 pub fn digest(data: &[u8]) -> [u8; 20] {
210 let mut h = Self::new();
211 h.update(data);
212 h.finalize()
213 }
214
215 fn finalize_into_reset(&mut self, out: &mut [u8; 20]) {
216 self.bit_len = self.bit_len.wrapping_add((self.pos as u64) * 8);
217
218 self.block[self.pos] = 0x80;
219 self.pos += 1;
220
221 if self.pos > 56 {
222 self.block[self.pos..].fill(0);
223 compress(&mut self.state, &self.block);
224 self.block = [0u8; 64];
225 self.pos = 0;
226 }
227
228 self.block[self.pos..56].fill(0);
229 self.block[56..].copy_from_slice(&self.bit_len.to_le_bytes());
230 compress(&mut self.state, &self.block);
231
232 for (chunk, word) in out.chunks_exact_mut(4).zip(self.state.iter()) {
233 chunk.copy_from_slice(&word.to_le_bytes());
234 }
235
236 self.zeroize();
237 }
238}
239
240impl Digest for Ripemd160 {
241 const BLOCK_LEN: usize = 64;
242 const OUTPUT_LEN: usize = 20;
243
244 fn new() -> Self {
245 Self::new()
246 }
247
248 fn update(&mut self, data: &[u8]) {
249 self.update(data);
250 }
251
252 fn finalize_into(self, out: &mut [u8]) {
253 assert_eq!(out.len(), 20, "wrong digest length");
254 out.copy_from_slice(&self.finalize());
255 }
256
257 fn finalize_reset(&mut self, out: &mut [u8]) {
258 let out: &mut [u8; 20] = out.try_into().expect("wrong digest length");
259 self.finalize_into_reset(out);
260 }
261
262 fn zeroize(&mut self) {
263 crate::ct::zeroize_slice(self.state.as_mut_slice());
264 crate::ct::zeroize_slice(self.block.as_mut_slice());
265 self.pos = 0;
266 self.bit_len = 0;
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 fn hex(bytes: &[u8]) -> String {
275 let mut out = String::with_capacity(bytes.len() * 2);
276 for b in bytes {
277 use core::fmt::Write;
278 let _ = write!(&mut out, "{b:02x}");
279 }
280 out
281 }
282
283 #[test]
284 fn ripemd160_empty() {
285 assert_eq!(
286 hex(&Ripemd160::digest(b"")),
287 "9c1185a5c5e9fc54612808977ee8f548b2258d31"
288 );
289 }
290
291 #[test]
292 fn ripemd160_abc() {
293 assert_eq!(
294 hex(&Ripemd160::digest(b"abc")),
295 "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"
296 );
297 }
298
299 #[test]
300 fn ripemd160_message_digest() {
301 assert_eq!(
302 hex(&Ripemd160::digest(b"message digest")),
303 "5d0689ef49d2fae572b881b123a85ffa21595f36"
304 );
305 }
306
307 #[test]
308 fn ripemd160_matches_openssl() {
309 let msg = b"The quick brown fox jumps over the lazy dog";
310 let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-ripemd160", "-binary"], msg) else {
311 return;
312 };
313 assert_eq!(Ripemd160::digest(msg).as_slice(), expected.as_slice());
314 }
315}