Skip to content

Commit c707065

Browse files
author
Palmer Cox
committed
MD5: Create an implementation of MD5.
1 parent a37f284 commit c707065

File tree

2 files changed

+331
-0
lines changed

2 files changed

+331
-0
lines changed

src/libextra/crypto/md5.rs

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

src/libextra/extra.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub mod treemap;
7171
mod cryptoutil;
7272
#[path="crypto/digest.rs"]
7373
pub mod digest;
74+
#[path="crypto/md5.rs"]
75+
pub mod md5;
7476
#[path="crypto/sha1.rs"]
7577
pub mod sha1;
7678
#[path="crypto/sha2.rs"]

0 commit comments

Comments
 (0)