]>
Commit | Line | Data |
---|---|---|
d76f7d0b XL |
1 | use std::io::prelude::*; |
2 | use std::io; | |
3ee932bc VK |
3 | use std::ops::{Deref, DerefMut}; |
4 | use std::fmt; | |
d76f7d0b XL |
5 | use ffi; |
6 | ||
7 | #[cfg(ossl110)] | |
8 | use ffi::{EVP_MD_CTX_new, EVP_MD_CTX_free}; | |
9 | #[cfg(any(ossl101, ossl102))] | |
10 | use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; | |
11 | ||
12 | use {cvt, cvt_p}; | |
13 | use error::ErrorStack; | |
14 | ||
15 | #[derive(Copy, Clone)] | |
16 | pub struct MessageDigest(*const ffi::EVP_MD); | |
17 | ||
18 | impl MessageDigest { | |
19 | pub fn md5() -> MessageDigest { | |
20 | unsafe { MessageDigest(ffi::EVP_md5()) } | |
21 | } | |
22 | ||
23 | pub fn sha1() -> MessageDigest { | |
24 | unsafe { MessageDigest(ffi::EVP_sha1()) } | |
25 | } | |
26 | ||
27 | pub fn sha224() -> MessageDigest { | |
28 | unsafe { MessageDigest(ffi::EVP_sha224()) } | |
29 | } | |
30 | ||
31 | pub fn sha256() -> MessageDigest { | |
32 | unsafe { MessageDigest(ffi::EVP_sha256()) } | |
33 | } | |
34 | ||
35 | pub fn sha384() -> MessageDigest { | |
36 | unsafe { MessageDigest(ffi::EVP_sha384()) } | |
37 | } | |
38 | ||
39 | pub fn sha512() -> MessageDigest { | |
40 | unsafe { MessageDigest(ffi::EVP_sha512()) } | |
41 | } | |
42 | ||
43 | pub fn ripemd160() -> MessageDigest { | |
44 | unsafe { MessageDigest(ffi::EVP_ripemd160()) } | |
45 | } | |
46 | ||
47 | pub fn as_ptr(&self) -> *const ffi::EVP_MD { | |
48 | self.0 | |
49 | } | |
50 | } | |
51 | ||
52 | #[derive(PartialEq, Copy, Clone)] | |
53 | enum State { | |
54 | Reset, | |
55 | Updated, | |
56 | Finalized, | |
57 | } | |
58 | ||
59 | use self::State::*; | |
60 | ||
61 | /// Provides message digest (hash) computation. | |
62 | /// | |
63 | /// # Examples | |
64 | /// | |
65 | /// Calculate a hash in one go: | |
66 | /// | |
67 | /// ``` | |
68 | /// use openssl::hash::{hash, MessageDigest}; | |
69 | /// | |
70 | /// let data = b"\x42\xF4\x97\xE0"; | |
71 | /// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; | |
72 | /// let res = hash(MessageDigest::md5(), data).unwrap(); | |
73 | /// assert_eq!(res, spec); | |
74 | /// ``` | |
75 | /// | |
76 | /// Supply the input in chunks: | |
77 | /// | |
78 | /// ``` | |
79 | /// use openssl::hash::{Hasher, MessageDigest}; | |
80 | /// | |
81 | /// let data = [b"\x42\xF4", b"\x97\xE0"]; | |
82 | /// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; | |
83 | /// let mut h = Hasher::new(MessageDigest::md5()).unwrap(); | |
84 | /// h.update(data[0]).unwrap(); | |
85 | /// h.update(data[1]).unwrap(); | |
86 | /// let res = h.finish().unwrap(); | |
87 | /// assert_eq!(res, spec); | |
88 | /// ``` | |
89 | /// | |
90 | /// # Warning | |
91 | /// | |
92 | /// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore. | |
93 | /// | |
94 | /// Don't ever hash passwords, use the functions in the `pkcs5` module or bcrypt/scrypt instead. | |
95 | pub struct Hasher { | |
96 | ctx: *mut ffi::EVP_MD_CTX, | |
97 | md: *const ffi::EVP_MD, | |
98 | type_: MessageDigest, | |
99 | state: State, | |
100 | } | |
101 | ||
102 | impl Hasher { | |
103 | /// Creates a new `Hasher` with the specified hash type. | |
104 | pub fn new(ty: MessageDigest) -> Result<Hasher, ErrorStack> { | |
105 | ffi::init(); | |
106 | ||
107 | let ctx = unsafe { try!(cvt_p(EVP_MD_CTX_new())) }; | |
108 | ||
109 | let mut h = Hasher { | |
110 | ctx: ctx, | |
111 | md: ty.as_ptr(), | |
112 | type_: ty, | |
113 | state: Finalized, | |
114 | }; | |
115 | try!(h.init()); | |
116 | Ok(h) | |
117 | } | |
118 | ||
119 | fn init(&mut self) -> Result<(), ErrorStack> { | |
120 | match self.state { | |
121 | Reset => return Ok(()), | |
122 | Updated => { | |
3ee932bc | 123 | try!(self.finish2()); |
d76f7d0b XL |
124 | } |
125 | Finalized => (), | |
126 | } | |
127 | unsafe { | |
128 | try!(cvt(ffi::EVP_DigestInit_ex(self.ctx, self.md, 0 as *mut _))); | |
129 | } | |
130 | self.state = Reset; | |
131 | Ok(()) | |
132 | } | |
133 | ||
134 | /// Feeds data into the hasher. | |
135 | pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { | |
136 | if self.state == Finalized { | |
137 | try!(self.init()); | |
138 | } | |
139 | unsafe { | |
82fc9e21 VK |
140 | try!(cvt(ffi::EVP_DigestUpdate( |
141 | self.ctx, | |
142 | data.as_ptr() as *mut _, | |
143 | data.len(), | |
144 | ))); | |
d76f7d0b XL |
145 | } |
146 | self.state = Updated; | |
147 | Ok(()) | |
148 | } | |
149 | ||
3ee932bc | 150 | #[deprecated(note = "use finish2 instead", since = "0.9.11")] |
d76f7d0b | 151 | pub fn finish(&mut self) -> Result<Vec<u8>, ErrorStack> { |
3ee932bc VK |
152 | self.finish2().map(|b| b.to_vec()) |
153 | } | |
154 | ||
155 | /// Returns the hash of the data written and resets the hasher. | |
156 | /// | |
157 | /// Unlike `finish`, this method does not allocate. | |
158 | pub fn finish2(&mut self) -> Result<DigestBytes, ErrorStack> { | |
d76f7d0b XL |
159 | if self.state == Finalized { |
160 | try!(self.init()); | |
161 | } | |
162 | unsafe { | |
163 | let mut len = ffi::EVP_MAX_MD_SIZE; | |
3ee932bc | 164 | let mut buf = [0; ffi::EVP_MAX_MD_SIZE as usize]; |
82fc9e21 VK |
165 | try!(cvt(ffi::EVP_DigestFinal_ex( |
166 | self.ctx, | |
167 | buf.as_mut_ptr(), | |
168 | &mut len, | |
169 | ))); | |
d76f7d0b | 170 | self.state = Finalized; |
3ee932bc VK |
171 | Ok(DigestBytes { |
172 | buf: buf, | |
173 | len: len as usize, | |
174 | }) | |
d76f7d0b XL |
175 | } |
176 | } | |
177 | } | |
178 | ||
179 | impl Write for Hasher { | |
180 | #[inline] | |
181 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
182 | try!(self.update(buf)); | |
183 | Ok(buf.len()) | |
184 | } | |
185 | ||
186 | fn flush(&mut self) -> io::Result<()> { | |
187 | Ok(()) | |
188 | } | |
189 | } | |
190 | ||
191 | impl Clone for Hasher { | |
192 | fn clone(&self) -> Hasher { | |
193 | let ctx = unsafe { | |
194 | let ctx = EVP_MD_CTX_new(); | |
195 | assert!(!ctx.is_null()); | |
196 | let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx); | |
197 | assert_eq!(r, 1); | |
198 | ctx | |
199 | }; | |
200 | Hasher { | |
201 | ctx: ctx, | |
202 | md: self.md, | |
203 | type_: self.type_, | |
204 | state: self.state, | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | impl Drop for Hasher { | |
210 | fn drop(&mut self) { | |
211 | unsafe { | |
212 | if self.state != Finalized { | |
3ee932bc | 213 | drop(self.finish2()); |
d76f7d0b XL |
214 | } |
215 | EVP_MD_CTX_free(self.ctx); | |
216 | } | |
217 | } | |
218 | } | |
219 | ||
3ee932bc VK |
220 | /// The resulting bytes of a digest. |
221 | /// | |
222 | /// This type derefs to a byte slice - it exists to avoid allocating memory to | |
223 | /// store the digest data. | |
224 | #[derive(Copy)] | |
225 | pub struct DigestBytes { | |
226 | buf: [u8; ffi::EVP_MAX_MD_SIZE as usize], | |
227 | len: usize, | |
228 | } | |
229 | ||
230 | impl Clone for DigestBytes { | |
231 | #[inline] | |
232 | fn clone(&self) -> DigestBytes { | |
233 | *self | |
234 | } | |
235 | } | |
236 | ||
237 | impl Deref for DigestBytes { | |
238 | type Target = [u8]; | |
239 | ||
240 | #[inline] | |
241 | fn deref(&self) -> &[u8] { | |
242 | &self.buf[..self.len] | |
243 | } | |
244 | } | |
245 | ||
246 | impl DerefMut for DigestBytes { | |
247 | #[inline] | |
248 | fn deref_mut(&mut self) -> &mut [u8] { | |
249 | &mut self.buf[..self.len] | |
250 | } | |
251 | } | |
252 | ||
253 | impl AsRef<[u8]> for DigestBytes { | |
254 | #[inline] | |
255 | fn as_ref(&self) -> &[u8] { | |
256 | self.deref() | |
257 | } | |
258 | } | |
259 | ||
260 | impl fmt::Debug for DigestBytes { | |
261 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
262 | fmt::Debug::fmt(&**self, fmt) | |
263 | } | |
264 | } | |
265 | ||
266 | #[deprecated(note = "use hash2 instead", since = "0.9.11")] | |
d76f7d0b | 267 | pub fn hash(t: MessageDigest, data: &[u8]) -> Result<Vec<u8>, ErrorStack> { |
3ee932bc VK |
268 | hash2(t, data).map(|b| b.to_vec()) |
269 | } | |
270 | ||
271 | /// Computes the hash of the `data` with the hash `t`. | |
272 | /// | |
273 | /// Unlike `hash`, this function does not allocate the return value. | |
274 | pub fn hash2(t: MessageDigest, data: &[u8]) -> Result<DigestBytes, ErrorStack> { | |
d76f7d0b XL |
275 | let mut h = try!(Hasher::new(t)); |
276 | try!(h.update(data)); | |
3ee932bc | 277 | h.finish2() |
d76f7d0b XL |
278 | } |
279 | ||
280 | #[cfg(test)] | |
281 | mod tests { | |
282 | use hex::{FromHex, ToHex}; | |
d76f7d0b XL |
283 | use std::io::prelude::*; |
284 | ||
3ee932bc VK |
285 | use super::*; |
286 | ||
d76f7d0b | 287 | fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { |
3ee932bc | 288 | let res = hash2(hashtype, &Vec::from_hex(hashtest.0).unwrap()).unwrap(); |
d76f7d0b XL |
289 | assert_eq!(res.to_hex(), hashtest.1); |
290 | } | |
291 | ||
292 | fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { | |
293 | let _ = h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap(); | |
3ee932bc | 294 | let res = h.finish2().unwrap(); |
d76f7d0b XL |
295 | assert_eq!(res.to_hex(), hashtest.1); |
296 | } | |
297 | ||
298 | // Test vectors from http://www.nsrl.nist.gov/testdata/ | |
299 | #[allow(non_upper_case_globals)] | |
300 | const md5_tests: [(&'static str, &'static str); 13] = | |
82fc9e21 VK |
301 | [ |
302 | ("", "d41d8cd98f00b204e9800998ecf8427e"), | |
303 | ("7F", "83acb6e67e50e31db6ed341dd2de1595"), | |
304 | ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"), | |
305 | ("FEE57A", "e0d583171eb06d56198fc0ef22173907"), | |
306 | ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"), | |
307 | ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"), | |
308 | ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"), | |
309 | ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"), | |
310 | ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"), | |
311 | ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"), | |
312 | ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"), | |
313 | ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), | |
314 | ( | |
315 | "AAED18DBE8938C19ED734A8D", | |
316 | "6f80fb775f27e0a4ce5c2f42fc72c5f1", | |
317 | ), | |
318 | ]; | |
d76f7d0b XL |
319 | |
320 | #[test] | |
321 | fn test_md5() { | |
322 | for test in md5_tests.iter() { | |
323 | hash_test(MessageDigest::md5(), test); | |
324 | } | |
325 | } | |
326 | ||
327 | #[test] | |
328 | fn test_md5_recycle() { | |
329 | let mut h = Hasher::new(MessageDigest::md5()).unwrap(); | |
330 | for test in md5_tests.iter() { | |
331 | hash_recycle_test(&mut h, test); | |
332 | } | |
333 | } | |
334 | ||
335 | #[test] | |
336 | fn test_finish_twice() { | |
337 | let mut h = Hasher::new(MessageDigest::md5()).unwrap(); | |
82fc9e21 VK |
338 | h.write_all(&Vec::from_hex(md5_tests[6].0).unwrap()) |
339 | .unwrap(); | |
3ee932bc VK |
340 | h.finish2().unwrap(); |
341 | let res = h.finish2().unwrap(); | |
342 | let null = hash2(MessageDigest::md5(), &[]).unwrap(); | |
343 | assert_eq!(&*res, &*null); | |
d76f7d0b XL |
344 | } |
345 | ||
346 | #[test] | |
347 | fn test_clone() { | |
348 | let i = 7; | |
349 | let inp = Vec::from_hex(md5_tests[i].0).unwrap(); | |
350 | assert!(inp.len() > 2); | |
351 | let p = inp.len() / 2; | |
352 | let h0 = Hasher::new(MessageDigest::md5()).unwrap(); | |
353 | ||
354 | println!("Clone a new hasher"); | |
355 | let mut h1 = h0.clone(); | |
356 | h1.write_all(&inp[..p]).unwrap(); | |
357 | { | |
358 | println!("Clone an updated hasher"); | |
359 | let mut h2 = h1.clone(); | |
360 | h2.write_all(&inp[p..]).unwrap(); | |
3ee932bc | 361 | let res = h2.finish2().unwrap(); |
d76f7d0b XL |
362 | assert_eq!(res.to_hex(), md5_tests[i].1); |
363 | } | |
364 | h1.write_all(&inp[p..]).unwrap(); | |
3ee932bc | 365 | let res = h1.finish2().unwrap(); |
d76f7d0b XL |
366 | assert_eq!(res.to_hex(), md5_tests[i].1); |
367 | ||
368 | println!("Clone a finished hasher"); | |
369 | let mut h3 = h1.clone(); | |
82fc9e21 VK |
370 | h3.write_all(&Vec::from_hex(md5_tests[i + 1].0).unwrap()) |
371 | .unwrap(); | |
3ee932bc | 372 | let res = h3.finish2().unwrap(); |
d76f7d0b XL |
373 | assert_eq!(res.to_hex(), md5_tests[i + 1].1); |
374 | } | |
375 | ||
376 | #[test] | |
377 | fn test_sha1() { | |
378 | let tests = [("616263", "a9993e364706816aba3e25717850c26c9cd0d89d")]; | |
379 | ||
380 | for test in tests.iter() { | |
381 | hash_test(MessageDigest::sha1(), test); | |
382 | } | |
383 | } | |
384 | ||
385 | #[test] | |
386 | fn test_sha256() { | |
82fc9e21 VK |
387 | let tests = [ |
388 | ( | |
389 | "616263", | |
390 | "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", | |
391 | ), | |
392 | ]; | |
d76f7d0b XL |
393 | |
394 | for test in tests.iter() { | |
395 | hash_test(MessageDigest::sha256(), test); | |
396 | } | |
397 | } | |
398 | ||
399 | #[test] | |
400 | fn test_ripemd160() { | |
401 | let tests = [("616263", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")]; | |
402 | ||
403 | for test in tests.iter() { | |
404 | hash_test(MessageDigest::ripemd160(), test); | |
405 | } | |
406 | } | |
407 | } |