Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

md5/
lib.rs

1//! The [MD5] hash function.
2//!
3//! ## Example
4//!
5//! ```
6//! let digest = md5::compute(b"abcdefghijklmnopqrstuvwxyz");
7//! assert_eq!(format!("{:x}", digest), "c3fcd3d76192e4007dfb496cca67e13b");
8//! ```
9//!
10//! ## Security Warning
11//!
12//! The package is provided for the purposes of interoperability with protocols
13//! and systems that mandate the use of MD5. However, MD5 should be considered
14//! [cryptographically broken and unsuitable for further use][VU836068].
15//! Collision attacks against MD5 are both practical and trivial, and
16//! [theoretical attacks against MD5 have been found][ACM1724151].
17//!
18//! [RFC6151] advises no new protocols to be designed with any MD5-based
19//! constructions, including HMAC-MD5.
20//!
21//! [MD5]: https://en.wikipedia.org/wiki/MD5
22//!
23//! [ACM1724151]: https://dl.acm.org/citation.cfm?id=1724151
24//! [RFC6151]: https://tools.ietf.org/html/rfc6151
25//! [VU836068]: https://www.kb.cert.org/vuls/id/836068
26
27// The implementation is based on:
28// https://www.ietf.org/rfc/rfc1321.txt
29
30#![cfg_attr(not(feature = "std"), no_std)]
31
32#[cfg(feature = "std")]
33use std as core;
34
35/// A digest.
36#[derive(Clone, Copy, Eq, Hash, PartialEq)]
37pub struct Digest(pub [u8; 16]);
38
39impl core::convert::From<Digest> for [u8; 16] {
40    #[inline]
41    fn from(digest: Digest) -> Self {
42        digest.0
43    }
44}
45
46impl core::fmt::Debug for Digest {
47    #[inline]
48    fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
49        core::fmt::LowerHex::fmt(self, formatter)
50    }
51}
52
53impl core::ops::Deref for Digest {
54    type Target = [u8; 16];
55
56    #[inline]
57    fn deref(&self) -> &Self::Target {
58        &self.0
59    }
60}
61
62impl core::ops::DerefMut for Digest {
63    #[inline]
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        &mut self.0
66    }
67}
68
69macro_rules! implement {
70    ($kind:ident, $format:expr) => {
71        impl core::fmt::$kind for Digest {
72            fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
73                for value in &self.0 {
74                    write!(formatter, $format, value)?;
75                }
76                Ok(())
77            }
78        }
79    };
80}
81
82implement!(LowerHex, "{:02x}");
83implement!(UpperHex, "{:02X}");
84
85/// A context.
86#[derive(Clone)]
87pub struct Context {
88    buffer: [u8; 64],
89    count: u64,
90    state: [u32; 4],
91}
92
93const PADDING: [u8; 64] = [
94    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98];
99
100impl Context {
101    /// Create a context for computing a digest.
102    #[inline]
103    pub fn new() -> Context {
104        Context {
105            buffer: [0; 64],
106            count: 0,
107            state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476],
108        }
109    }
110
111    /// Consume data.
112    #[inline]
113    pub fn consume<T: AsRef<[u8]>>(&mut self, data: T) {
114        consume(self, data.as_ref());
115    }
116
117    /// Finalize and return the digest.
118    #[rustfmt::skip]
119    #[allow(clippy::double_parens, clippy::needless_range_loop)]
120    pub fn finalize(mut self) -> Digest {
121        let k = ((self.count >> 3) & 0x3f) as usize;
122        let length = self.count.to_le_bytes();
123        consume(
124            &mut self,
125            &PADDING[..(if k < 56 { 56 - k } else { 120 - k })],
126        );
127        self.buffer[56..64].copy_from_slice(&length);
128        transform(&mut self.state, &self.buffer);
129        let mut digest = [0u8; 16];
130        let mut j = 0;
131        for i in 0..4 {
132            digest[j    ] = ((self.state[i]      ) & 0xff) as u8;
133            digest[j + 1] = ((self.state[i] >>  8) & 0xff) as u8;
134            digest[j + 2] = ((self.state[i] >> 16) & 0xff) as u8;
135            digest[j + 3] = ((self.state[i] >> 24) & 0xff) as u8;
136            j += 4;
137        }
138        Digest(digest)
139    }
140
141    /// Finalize and return the digest.
142    #[deprecated(since = "0.8.0", note = "Use `finalize`.")]
143    #[inline]
144    pub fn compute(self) -> Digest {
145        self.finalize()
146    }
147}
148
149impl Default for Context {
150    #[inline]
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156impl core::convert::From<Context> for Digest {
157    #[inline]
158    fn from(context: Context) -> Digest {
159        context.finalize()
160    }
161}
162
163#[cfg(feature = "std")]
164impl core::io::Write for Context {
165    #[inline]
166    fn write(&mut self, data: &[u8]) -> core::io::Result<usize> {
167        self.consume(data);
168        Ok(data.len())
169    }
170
171    #[inline]
172    fn flush(&mut self) -> core::io::Result<()> {
173        Ok(())
174    }
175}
176
177/// Compute the digest of data.
178#[inline]
179pub fn compute<T: AsRef<[u8]>>(data: T) -> Digest {
180    let mut context = Context::new();
181    context.consume(data);
182    context.finalize()
183}
184
185#[rustfmt::skip]
186#[inline(always)]
187fn consume(context: &mut Context, mut data: &[u8]) {
188    let k = ((context.count >> 3) & 0x3f) as usize;
189    context.count = context.count.wrapping_add((data.len() as u64) << 3);
190    if k != 0 {
191        let n = core::cmp::min(64 - k, data.len());
192        context.buffer[k..k + n].copy_from_slice(&data[..n]);
193        data = &data[n..];
194        if k + n != 64 {
195            return;
196        }
197        transform(&mut context.state, &context.buffer);
198    }
199    while data.len() >= 64 {
200        let block = <&[u8; 64]>::try_from(&data[..64]).unwrap();
201        transform(&mut context.state, block);
202        data = &data[64..];
203    }
204    context.buffer[..data.len()].copy_from_slice(data);
205}
206
207#[rustfmt::skip]
208#[inline(always)]
209fn transform(state: &mut [u32; 4], block: &[u8; 64]) {
210    macro_rules! decode(
211        ($i:expr) => (u32::from_le_bytes([block[$i], block[$i + 1], block[$i + 2], block[$i + 3]]));
212    );
213    let input = [
214        decode!( 0), decode!( 4), decode!( 8), decode!(12),
215        decode!(16), decode!(20), decode!(24), decode!(28),
216        decode!(32), decode!(36), decode!(40), decode!(44),
217        decode!(48), decode!(52), decode!(56), decode!(60),
218    ];
219    let (mut a, mut b, mut c, mut d) = (state[0], state[1], state[2], state[3]);
220    macro_rules! add(
221        ($a:expr, $b:expr) => ($a.wrapping_add($b));
222    );
223    macro_rules! rotate(
224        ($x:expr, $n:expr) => ($x.rotate_left($n));
225    );
226    {
227        macro_rules! F(
228            ($x:expr, $y:expr, $z:expr) => ($z ^ ($x & ($y ^ $z)));
229        );
230        macro_rules! T(
231            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
232                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
233                $a = rotate!($a, $s);
234                $a = add!($a, $b);
235            });
236        );
237        const S1: u32 =  7;
238        const S2: u32 = 12;
239        const S3: u32 = 17;
240        const S4: u32 = 22;
241        T!(a, b, c, d, input[ 0], S1, 3614090360);
242        T!(d, a, b, c, input[ 1], S2, 3905402710);
243        T!(c, d, a, b, input[ 2], S3,  606105819);
244        T!(b, c, d, a, input[ 3], S4, 3250441966);
245        T!(a, b, c, d, input[ 4], S1, 4118548399);
246        T!(d, a, b, c, input[ 5], S2, 1200080426);
247        T!(c, d, a, b, input[ 6], S3, 2821735955);
248        T!(b, c, d, a, input[ 7], S4, 4249261313);
249        T!(a, b, c, d, input[ 8], S1, 1770035416);
250        T!(d, a, b, c, input[ 9], S2, 2336552879);
251        T!(c, d, a, b, input[10], S3, 4294925233);
252        T!(b, c, d, a, input[11], S4, 2304563134);
253        T!(a, b, c, d, input[12], S1, 1804603682);
254        T!(d, a, b, c, input[13], S2, 4254626195);
255        T!(c, d, a, b, input[14], S3, 2792965006);
256        T!(b, c, d, a, input[15], S4, 1236535329);
257    }
258    {
259        macro_rules! F(
260            ($x:expr, $y:expr, $z:expr) => ($y ^ ($z & ($x ^ $y)));
261        );
262        macro_rules! T(
263            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
264                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
265                $a = rotate!($a, $s);
266                $a = add!($a, $b);
267            });
268        );
269        const S1: u32 =  5;
270        const S2: u32 =  9;
271        const S3: u32 = 14;
272        const S4: u32 = 20;
273        T!(a, b, c, d, input[ 1], S1, 4129170786);
274        T!(d, a, b, c, input[ 6], S2, 3225465664);
275        T!(c, d, a, b, input[11], S3,  643717713);
276        T!(b, c, d, a, input[ 0], S4, 3921069994);
277        T!(a, b, c, d, input[ 5], S1, 3593408605);
278        T!(d, a, b, c, input[10], S2,   38016083);
279        T!(c, d, a, b, input[15], S3, 3634488961);
280        T!(b, c, d, a, input[ 4], S4, 3889429448);
281        T!(a, b, c, d, input[ 9], S1,  568446438);
282        T!(d, a, b, c, input[14], S2, 3275163606);
283        T!(c, d, a, b, input[ 3], S3, 4107603335);
284        T!(b, c, d, a, input[ 8], S4, 1163531501);
285        T!(a, b, c, d, input[13], S1, 2850285829);
286        T!(d, a, b, c, input[ 2], S2, 4243563512);
287        T!(c, d, a, b, input[ 7], S3, 1735328473);
288        T!(b, c, d, a, input[12], S4, 2368359562);
289    }
290    {
291        macro_rules! F(
292            ($x:expr, $y:expr, $z:expr) => ($x ^ $y ^ $z);
293        );
294        macro_rules! T(
295            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
296                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
297                $a = rotate!($a, $s);
298                $a = add!($a, $b);
299            });
300        );
301        const S1: u32 =  4;
302        const S2: u32 = 11;
303        const S3: u32 = 16;
304        const S4: u32 = 23;
305        T!(a, b, c, d, input[ 5], S1, 4294588738);
306        T!(d, a, b, c, input[ 8], S2, 2272392833);
307        T!(c, d, a, b, input[11], S3, 1839030562);
308        T!(b, c, d, a, input[14], S4, 4259657740);
309        T!(a, b, c, d, input[ 1], S1, 2763975236);
310        T!(d, a, b, c, input[ 4], S2, 1272893353);
311        T!(c, d, a, b, input[ 7], S3, 4139469664);
312        T!(b, c, d, a, input[10], S4, 3200236656);
313        T!(a, b, c, d, input[13], S1,  681279174);
314        T!(d, a, b, c, input[ 0], S2, 3936430074);
315        T!(c, d, a, b, input[ 3], S3, 3572445317);
316        T!(b, c, d, a, input[ 6], S4,   76029189);
317        T!(a, b, c, d, input[ 9], S1, 3654602809);
318        T!(d, a, b, c, input[12], S2, 3873151461);
319        T!(c, d, a, b, input[15], S3,  530742520);
320        T!(b, c, d, a, input[ 2], S4, 3299628645);
321    }
322    {
323        macro_rules! F(
324            ($x:expr, $y:expr, $z:expr) => ($y ^ ($x | !$z));
325        );
326        macro_rules! T(
327            ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({
328                $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac);
329                $a = rotate!($a, $s);
330                $a = add!($a, $b);
331            });
332        );
333        const S1: u32 =  6;
334        const S2: u32 = 10;
335        const S3: u32 = 15;
336        const S4: u32 = 21;
337        T!(a, b, c, d, input[ 0], S1, 4096336452);
338        T!(d, a, b, c, input[ 7], S2, 1126891415);
339        T!(c, d, a, b, input[14], S3, 2878612391);
340        T!(b, c, d, a, input[ 5], S4, 4237533241);
341        T!(a, b, c, d, input[12], S1, 1700485571);
342        T!(d, a, b, c, input[ 3], S2, 2399980690);
343        T!(c, d, a, b, input[10], S3, 4293915773);
344        T!(b, c, d, a, input[ 1], S4, 2240044497);
345        T!(a, b, c, d, input[ 8], S1, 1873313359);
346        T!(d, a, b, c, input[15], S2, 4264355552);
347        T!(c, d, a, b, input[ 6], S3, 2734768916);
348        T!(b, c, d, a, input[13], S4, 1309151649);
349        T!(a, b, c, d, input[ 4], S1, 4149444226);
350        T!(d, a, b, c, input[11], S2, 3174756917);
351        T!(c, d, a, b, input[ 2], S3,  718787259);
352        T!(b, c, d, a, input[ 9], S4, 3951481745);
353    }
354    state[0] = add!(state[0], a);
355    state[1] = add!(state[1], b);
356    state[2] = add!(state[2], c);
357    state[3] = add!(state[3], d);
358}
359
360#[cfg(test)]
361mod tests {
362    use std::io::prelude::Write;
363
364    use super::Context;
365
366    #[test]
367    fn compute() {
368        let inputs = [
369            "",
370            "a",
371            "abc",
372            "message digest",
373            "abcdefghijklmnopqrstuvwxyz",
374            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
375            "0123456789012345678901234567890123456789012345678901234567890123",
376            "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
377        ];
378        let outputs = [
379            "d41d8cd98f00b204e9800998ecf8427e",
380            "0cc175b9c0f1b6a831c399e269772661",
381            "900150983cd24fb0d6963f7d28e17f72",
382            "f96b697d7cb7938d525a2f31aaf161d0",
383            "c3fcd3d76192e4007dfb496cca67e13b",
384            "d174ab98d277d9f5a5611c2c9f419d9f",
385            "7f7bfd348709deeaace19e3f535f8c54",
386            "57edf4a22be3c955ac49da2e2107b67a",
387        ];
388        for (input, &output) in inputs.iter().zip(outputs.iter()) {
389            let digest = super::compute(input);
390            assert_eq!(format!("{digest:x}"), output);
391
392            let mut context = Context::new();
393            context.consume(input);
394            let digest = context.finalize();
395            assert_eq!(format!("{digest:x}"), output);
396        }
397    }
398
399    #[test]
400    fn consume() {
401        for len in [0, 1, 55, 56, 57, 63, 64, 65, 119, 120, 121, 1024] {
402            let data: Vec<_> = (0..len).map(|i| (i % 251) as u8).collect();
403            let expected = super::compute(&data);
404            for chunk_size in [1, 7, 63, 64, 65, 128] {
405                let mut context = Context::new();
406                for chunk in data.chunks(chunk_size) {
407                    context.consume(chunk);
408                }
409                assert_eq!(context.finalize(), expected);
410            }
411        }
412    }
413
414    #[test]
415    fn index() {
416        let mut digest = super::compute(b"abc");
417        assert_eq!(digest[0], 0x90);
418        assert_eq!(&digest[0], &0x90);
419        assert_eq!(&mut digest[0], &mut 0x90);
420    }
421
422    #[test]
423    fn write_29() {
424        let data = vec![0; 8 * 1024 * 1024];
425        let mut context = Context::new();
426        for _ in 0..64 {
427            context.write(&data).unwrap();
428        }
429        assert_eq!(
430            format!("{:x}", context.finalize()),
431            "aa559b4e3523a6c931f08f4df52d58f2",
432        );
433    }
434
435    #[cfg(target_pointer_width = "64")]
436    #[test]
437    fn write_32() {
438        let data = vec![0; std::u32::MAX as usize + 1];
439        let mut context = Context::new();
440        context.write(&data).unwrap();
441        assert_eq!(
442            format!("{:x}", context.finalize()),
443            "c9a5a6878d97b48cc965c1e41859f034",
444        );
445    }
446}