crypto/
md5.rs

1// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use cryptoutil::{write_u32_le, read_u32v_le, FixedBuffer, FixedBuffer64, StandardPadding};
12use digest::Digest;
13use step_by::RangeExt;
14
15
16// A structure that represents that state of a digest computation for the MD5 digest function
17#[derive(Clone, Copy)]
18struct Md5State {
19    s0: u32,
20    s1: u32,
21    s2: u32,
22    s3: u32
23}
24
25impl Md5State {
26    fn new() -> Md5State {
27        Md5State {
28            s0: 0x67452301,
29            s1: 0xefcdab89,
30            s2: 0x98badcfe,
31            s3: 0x10325476
32        }
33    }
34
35    fn reset(&mut self) {
36        self.s0 = 0x67452301;
37        self.s1 = 0xefcdab89;
38        self.s2 = 0x98badcfe;
39        self.s3 = 0x10325476;
40    }
41
42    fn process_block(&mut self, input: &[u8]) {
43        fn f(u: u32, v: u32, w: u32) -> u32 {
44            (u & v) | (!u & w)
45        }
46
47        fn g(u: u32, v: u32, w: u32) -> u32 {
48            (u & w) | (v & !w)
49        }
50
51        fn h(u: u32, v: u32, w: u32) -> u32 {
52            u ^ v ^ w
53        }
54
55        fn i(u: u32, v: u32, w: u32) -> u32 {
56            v ^ (u | !w)
57        }
58
59        fn op_f(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
60            w.wrapping_add(f(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x)
61        }
62
63        fn op_g(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
64            w.wrapping_add(g(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x)
65        }
66
67        fn op_h(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
68            w.wrapping_add(h(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x)
69        }
70
71        fn op_i(w: u32, x: u32, y: u32, z: u32, m: u32, s: u32) -> u32 {
72            w.wrapping_add(i(x, y, z)).wrapping_add(m).rotate_left(s).wrapping_add(x)
73        }
74
75        let mut a = self.s0;
76        let mut b = self.s1;
77        let mut c = self.s2;
78        let mut d = self.s3;
79
80        let mut data = [0u32; 16];
81
82        read_u32v_le(&mut data, input);
83
84        // round 1
85        for i in (0..16).step_up(4) {
86            a = op_f(a, b, c, d, data[i].wrapping_add(C1[i]), 7);
87            d = op_f(d, a, b, c, data[i + 1].wrapping_add(C1[i + 1]), 12);
88            c = op_f(c, d, a, b, data[i + 2].wrapping_add(C1[i + 2]), 17);
89            b = op_f(b, c, d, a, data[i + 3].wrapping_add(C1[i + 3]), 22);
90        }
91
92        // round 2
93        let mut t = 1;
94        for i in (0..16).step_up(4) {
95            a = op_g(a, b, c, d, data[t & 0x0f].wrapping_add(C2[i]), 5);
96            d = op_g(d, a, b, c, data[(t + 5) & 0x0f].wrapping_add(C2[i + 1]), 9);
97            c = op_g(c, d, a, b, data[(t + 10) & 0x0f].wrapping_add(C2[i + 2]), 14);
98            b = op_g(b, c, d, a, data[(t + 15) & 0x0f].wrapping_add(C2[i + 3]), 20);
99            t += 20;
100        }
101
102        // round 3
103        t = 5;
104        for i in (0..16).step_up(4) {
105            a = op_h(a, b, c, d, data[t & 0x0f].wrapping_add(C3[i]), 4);
106            d = op_h(d, a, b, c, data[(t + 3) & 0x0f].wrapping_add(C3[i + 1]), 11);
107            c = op_h(c, d, a, b, data[(t + 6) & 0x0f].wrapping_add(C3[i + 2]), 16);
108            b = op_h(b, c, d, a, data[(t + 9) & 0x0f].wrapping_add(C3[i + 3]), 23);
109            t += 12;
110        }
111
112        // round 4
113        t = 0;
114        for i in (0..16).step_up(4) {
115            a = op_i(a, b, c, d, data[t & 0x0f].wrapping_add(C4[i]), 6);
116            d = op_i(d, a, b, c, data[(t + 7) & 0x0f].wrapping_add(C4[i + 1]), 10);
117            c = op_i(c, d, a, b, data[(t + 14) & 0x0f].wrapping_add(C4[i + 2]), 15);
118            b = op_i(b, c, d, a, data[(t + 21) & 0x0f].wrapping_add(C4[i + 3]), 21);
119            t += 28;
120        }
121
122        self.s0 = self.s0.wrapping_add(a);
123        self.s1 = self.s1.wrapping_add(b);
124        self.s2 = self.s2.wrapping_add(c);
125        self.s3 = self.s3.wrapping_add(d);
126    }
127}
128
129// Round 1 constants
130static C1: [u32; 16] = [
131    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
132    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821
133];
134
135// Round 2 constants
136static C2: [u32; 16] = [
137    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
138    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a
139];
140
141// Round 3 constants
142static C3: [u32; 16] = [
143    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
144    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665
145];
146
147// Round 4 constants
148static C4: [u32; 16] = [
149    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
150    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
151];
152
153
154/// The MD5 Digest algorithm
155#[derive(Clone, Copy)]
156pub struct Md5 {
157    length_bytes: u64,
158    buffer: FixedBuffer64,
159    state: Md5State,
160    finished: bool,
161}
162
163impl Md5 {
164    /// Construct a new instance of the MD5 Digest.
165    pub fn new() -> Md5 {
166        Md5 {
167            length_bytes: 0,
168            buffer: FixedBuffer64::new(),
169            state: Md5State::new(),
170            finished: false
171        }
172    }
173}
174
175impl Digest for Md5 {
176    fn input(&mut self, input: &[u8]) {
177        assert!(!self.finished);
178        // Unlike Sha1 and Sha2, the length value in MD5 is defined as the length of the message mod
179        // 2^64 - ie: integer overflow is OK.
180        self.length_bytes += input.len() as u64;
181        let self_state = &mut self.state;
182        self.buffer.input(input, |d: &[u8]| { self_state.process_block(d);}
183        );
184    }
185
186    fn reset(&mut self) {
187        self.length_bytes = 0;
188        self.buffer.reset();
189        self.state.reset();
190        self.finished = false;
191    }
192
193    fn result(&mut self, out: &mut [u8]) {
194        if !self.finished {
195            let self_state = &mut self.state;
196            self.buffer.standard_padding(8, |d: &[u8]| { self_state.process_block(d); });
197            write_u32_le(self.buffer.next(4), (self.length_bytes << 3) as u32);
198            write_u32_le(self.buffer.next(4), (self.length_bytes >> 29) as u32);
199            self_state.process_block(self.buffer.full_buffer());
200            self.finished = true;
201        }
202
203        write_u32_le(&mut out[0..4], self.state.s0);
204        write_u32_le(&mut out[4..8], self.state.s1);
205        write_u32_le(&mut out[8..12], self.state.s2);
206        write_u32_le(&mut out[12..16], self.state.s3);
207    }
208
209    fn output_bits(&self) -> usize { 128 }
210
211    fn block_size(&self) -> usize { 64 }
212}
213
214
215#[cfg(test)]
216mod tests {
217    use cryptoutil::test::test_digest_1million_random;
218    use digest::Digest;
219    use md5::Md5;
220
221
222    struct Test {
223        input: &'static str,
224        output_str: &'static str,
225    }
226
227    fn test_hash<D: Digest>(sh: &mut D, tests: &[Test]) {
228        // Test that it works when accepting the message all at once
229        for t in tests.iter() {
230            sh.input_str(t.input);
231
232            let out_str = sh.result_str();
233            assert_eq!(out_str, t.output_str);
234
235            sh.reset();
236        }
237
238        // Test that it works when accepting the message in pieces
239        for t in tests.iter() {
240            let len = t.input.len();
241            let mut left = len;
242            while left > 0 {
243                let take = (left + 1) / 2;
244                sh.input_str(&t.input[len - left..take + len - left]);
245                left = left - take;
246            }
247
248            let out_str = sh.result_str();
249            assert_eq!(out_str, t.output_str);
250
251            sh.reset();
252        }
253    }
254
255    #[test]
256    fn test_md5() {
257        // Examples from wikipedia
258        let wikipedia_tests = vec![
259            Test {
260                input: "",
261                output_str: "d41d8cd98f00b204e9800998ecf8427e"
262            },
263            Test {
264                input: "The quick brown fox jumps over the lazy dog",
265                output_str: "9e107d9d372bb6826bd81d3542a419d6"
266            },
267            Test {
268                input: "The quick brown fox jumps over the lazy dog.",
269                output_str: "e4d909c290d0fb1ca068ffaddf22cbd0"
270            },
271        ];
272
273        let tests = wikipedia_tests;
274
275        let mut sh = Md5::new();
276
277        test_hash(&mut sh, &tests[..]);
278    }
279
280    #[test]
281    fn test_1million_random_md5() {
282        let mut sh = Md5::new();
283        test_digest_1million_random(
284            &mut sh,
285            64,
286            "7707d6ae4e027c70eea2a935c2296f21");
287    }
288}
289
290
291#[cfg(all(test, feature = "with-bench"))]
292mod bench {
293    use test::Bencher;
294
295    use digest::Digest;
296    use md5::Md5;
297
298
299    #[bench]
300    pub fn md5_10(bh: & mut Bencher) {
301        let mut sh = Md5::new();
302        let bytes = [1u8; 10];
303        bh.iter( || {
304            sh.input(&bytes);
305        });
306        bh.bytes = bytes.len() as u64;
307    }
308
309    #[bench]
310    pub fn md5_1k(bh: & mut Bencher) {
311        let mut sh = Md5::new();
312        let bytes = [1u8; 1024];
313        bh.iter( || {
314            sh.input(&bytes);
315        });
316        bh.bytes = bytes.len() as u64;
317    }
318
319    #[bench]
320    pub fn md5_64k(bh: & mut Bencher) {
321        let mut sh = Md5::new();
322        let bytes = [1u8; 65536];
323        bh.iter( || {
324            sh.input(&bytes);
325        });
326        bh.bytes = bytes.len() as u64;
327    }
328}