1use crate::ffi;
2use std::convert::TryInto;
3use std::ffi::{c_uint, c_void};
4use std::fmt;
5use std::io;
6use std::io::prelude::*;
7use std::ops::{Deref, DerefMut};
8use std::ptr;
9
10use crate::error::ErrorStack;
11use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
12use crate::nid::Nid;
13use crate::{cvt, cvt_p};
14
15#[derive(Copy, Clone, PartialEq, Eq)]
16pub struct MessageDigest(*const ffi::EVP_MD);
17
18impl MessageDigest {
19 pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self {
25 MessageDigest(x)
26 }
27
28 pub fn from_nid(type_: Nid) -> Option<MessageDigest> {
34 unsafe {
35 let ptr = ffi::EVP_get_digestbynid(type_.as_raw());
36 if ptr.is_null() {
37 None
38 } else {
39 Some(MessageDigest(ptr))
40 }
41 }
42 }
43
44 pub fn md5() -> MessageDigest {
45 unsafe { MessageDigest(ffi::EVP_md5()) }
46 }
47
48 pub fn sha1() -> MessageDigest {
49 unsafe { MessageDigest(ffi::EVP_sha1()) }
50 }
51
52 pub fn sha224() -> MessageDigest {
53 unsafe { MessageDigest(ffi::EVP_sha224()) }
54 }
55
56 pub fn sha256() -> MessageDigest {
57 unsafe { MessageDigest(ffi::EVP_sha256()) }
58 }
59
60 pub fn sha384() -> MessageDigest {
61 unsafe { MessageDigest(ffi::EVP_sha384()) }
62 }
63
64 pub fn sha512() -> MessageDigest {
65 unsafe { MessageDigest(ffi::EVP_sha512()) }
66 }
67
68 pub fn sha512_256() -> MessageDigest {
69 unsafe { MessageDigest(ffi::EVP_sha512_256()) }
70 }
71
72 #[allow(clippy::trivially_copy_pass_by_ref)]
73 pub fn as_ptr(&self) -> *const ffi::EVP_MD {
74 self.0
75 }
76
77 #[allow(clippy::trivially_copy_pass_by_ref)]
79 pub fn size(&self) -> usize {
80 unsafe { ffi::EVP_MD_size(self.0) }
81 }
82
83 #[allow(clippy::trivially_copy_pass_by_ref)]
85 pub fn type_(&self) -> Nid {
86 Nid::from_raw(unsafe { ffi::EVP_MD_type(self.0) })
87 }
88}
89
90unsafe impl Sync for MessageDigest {}
91unsafe impl Send for MessageDigest {}
92
93#[derive(PartialEq, Copy, Clone)]
94enum State {
95 Reset,
96 Updated,
97 Finalized,
98}
99
100use self::State::*;
101
102pub struct Hasher {
140 ctx: *mut ffi::EVP_MD_CTX,
141 md: *const ffi::EVP_MD,
142 type_: MessageDigest,
143 state: State,
144}
145
146unsafe impl Sync for Hasher {}
147unsafe impl Send for Hasher {}
148
149impl Hasher {
150 pub fn new(ty: MessageDigest) -> Result<Hasher, ErrorStack> {
152 ffi::init();
153
154 let ctx = unsafe { cvt_p(EVP_MD_CTX_new())? };
155
156 let mut h = Hasher {
157 ctx,
158 md: ty.as_ptr(),
159 type_: ty,
160 state: Finalized,
161 };
162 h.init()?;
163 Ok(h)
164 }
165
166 fn init(&mut self) -> Result<(), ErrorStack> {
167 match self.state {
168 Reset => return Ok(()),
169 Updated => {
170 self.finish()?;
171 }
172 Finalized => (),
173 }
174 unsafe {
175 cvt(ffi::EVP_DigestInit_ex(self.ctx, self.md, ptr::null_mut()))?;
176 }
177 self.state = Reset;
178 Ok(())
179 }
180
181 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
183 if self.state == Finalized {
184 self.init()?;
185 }
186 unsafe {
187 cvt(ffi::EVP_DigestUpdate(
188 self.ctx,
189 data.as_ptr() as *mut _,
190 data.len(),
191 ))?;
192 }
193 self.state = Updated;
194 Ok(())
195 }
196
197 pub fn finish(&mut self) -> Result<DigestBytes, ErrorStack> {
199 if self.state == Finalized {
200 self.init()?;
201 }
202 unsafe {
203 let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap();
204 let mut buf = [0; ffi::EVP_MAX_MD_SIZE as usize];
205 cvt(ffi::EVP_DigestFinal_ex(
206 self.ctx,
207 buf.as_mut_ptr(),
208 &mut len,
209 ))?;
210 self.state = Finalized;
211 Ok(DigestBytes {
212 buf,
213 len: len as usize,
214 })
215 }
216 }
217
218 pub fn finish_xof(&mut self, buf: &mut [u8]) -> Result<(), ErrorStack> {
221 if self.state == Finalized {
222 self.init()?;
223 }
224 unsafe {
225 cvt(ffi::EVP_DigestFinalXOF(
226 self.ctx,
227 buf.as_mut_ptr(),
228 buf.len(),
229 ))?;
230 self.state = Finalized;
231 Ok(())
232 }
233 }
234}
235
236impl Write for Hasher {
237 #[inline]
238 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
239 self.update(buf)?;
240 Ok(buf.len())
241 }
242
243 fn flush(&mut self) -> io::Result<()> {
244 Ok(())
245 }
246}
247
248impl Clone for Hasher {
249 fn clone(&self) -> Hasher {
250 let ctx = unsafe {
251 let ctx = EVP_MD_CTX_new();
252 assert!(!ctx.is_null());
253 let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx);
254 assert_eq!(r, 1);
255 ctx
256 };
257 Hasher {
258 ctx,
259 md: self.md,
260 type_: self.type_,
261 state: self.state,
262 }
263 }
264}
265
266impl Drop for Hasher {
267 fn drop(&mut self) {
268 unsafe {
269 if self.state != Finalized {
270 drop(self.finish());
271 }
272 EVP_MD_CTX_free(self.ctx);
273 }
274 }
275}
276
277#[derive(Copy)]
282pub struct DigestBytes {
283 pub(crate) buf: [u8; ffi::EVP_MAX_MD_SIZE as usize],
284 pub(crate) len: usize,
285}
286
287impl Clone for DigestBytes {
288 #[inline]
289 fn clone(&self) -> DigestBytes {
290 *self
291 }
292}
293
294impl Deref for DigestBytes {
295 type Target = [u8];
296
297 #[inline]
298 fn deref(&self) -> &[u8] {
299 &self.buf[..self.len]
300 }
301}
302
303impl DerefMut for DigestBytes {
304 #[inline]
305 fn deref_mut(&mut self) -> &mut [u8] {
306 &mut self.buf[..self.len]
307 }
308}
309
310impl AsRef<[u8]> for DigestBytes {
311 #[inline]
312 fn as_ref(&self) -> &[u8] {
313 self.deref()
314 }
315}
316
317impl fmt::Debug for DigestBytes {
318 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
319 fmt::Debug::fmt(&**self, fmt)
320 }
321}
322
323pub fn hash(t: MessageDigest, data: &[u8]) -> Result<DigestBytes, ErrorStack> {
325 let mut h = Hasher::new(t)?;
326 h.update(data)?;
327 h.finish()
328}
329
330pub fn hash_xof(t: MessageDigest, data: &[u8], buf: &mut [u8]) -> Result<(), ErrorStack> {
332 let mut h = Hasher::new(t)?;
333 h.update(data)?;
334 h.finish_xof(buf)
335}
336
337pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; 32], ErrorStack> {
339 hmac(MessageDigest::sha256(), key, data)
340}
341
342pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; 64], ErrorStack> {
344 hmac(MessageDigest::sha512(), key, data)
345}
346
347pub fn hmac_sha1(key: &[u8], data: &[u8]) -> Result<[u8; 20], ErrorStack> {
349 hmac(MessageDigest::sha1(), key, data)
350}
351
352fn hmac<const N: usize>(
353 digest: MessageDigest,
354 key: &[u8],
355 data: &[u8],
356) -> Result<[u8; N], ErrorStack> {
357 let mut out = [0u8; N];
358 let mut out_len: c_uint = 0;
359
360 cvt_p(unsafe {
361 ffi::HMAC(
362 digest.as_ptr(),
363 key.as_ptr() as *const c_void,
364 key.len(),
365 data.as_ptr(),
366 data.len(),
367 out.as_mut_ptr(),
368 &mut out_len,
369 )
370 })?;
371
372 assert_eq!(out_len as usize, N);
373
374 Ok(out)
375}
376
377#[cfg(test)]
378mod tests {
379 use hex::{self, FromHex};
380 use std::io::prelude::*;
381
382 use super::*;
383
384 fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) {
385 let res = hash(hashtype, &Vec::from_hex(hashtest.0).unwrap()).unwrap();
386 assert_eq!(hex::encode(res), hashtest.1);
387 }
388
389 fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) {
390 h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap();
391 let res = h.finish().unwrap();
392 assert_eq!(hex::encode(res), hashtest.1);
393 }
394
395 const MD5_TESTS: [(&str, &str); 13] = [
397 ("", "d41d8cd98f00b204e9800998ecf8427e"),
398 ("7F", "83acb6e67e50e31db6ed341dd2de1595"),
399 ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"),
400 ("FEE57A", "e0d583171eb06d56198fc0ef22173907"),
401 ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"),
402 ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"),
403 ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"),
404 ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"),
405 ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"),
406 ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"),
407 ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"),
408 ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"),
409 (
410 "AAED18DBE8938C19ED734A8D",
411 "6f80fb775f27e0a4ce5c2f42fc72c5f1",
412 ),
413 ];
414
415 #[test]
416 fn test_hmac_sha256() {
417 let hmac = hmac_sha256(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
418
419 assert_eq!(
420 hmac,
421 [
422 0x50, 0xbb, 0x7d, 0xd2, 0xb8, 0xd2, 0x51, 0x5d, 0xb4, 0x2b, 0x70, 0xc3, 0x0b, 0xfd,
423 0xf5, 0x4c, 0x38, 0xa7, 0xae, 0x99, 0x07, 0xe5, 0x80, 0x0f, 0x8b, 0xe8, 0x34, 0x83,
424 0x55, 0x5f, 0xd0, 0xd4
425 ]
426 );
427 }
428
429 #[test]
430 fn test_hmac_sha512() {
431 let hmac = hmac_sha512(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
432
433 assert_eq!(
434 hmac,
435 [
436 0xc2, 0x7a, 0x7f, 0x7c, 0x17, 0x4c, 0x87, 0x70, 0x7f, 0x8c, 0xb7, 0x90, 0x01, 0xba,
437 0x23, 0x0e, 0xb7, 0xd6, 0x1a, 0xfd, 0x50, 0xea, 0x40, 0x43, 0x5f, 0x03, 0x25, 0x5a,
438 0x22, 0xb7, 0x8d, 0x0e, 0xba, 0x0d, 0x47, 0xb8, 0xef, 0xaa, 0xbf, 0xb1, 0xe7, 0xad,
439 0xc5, 0xd1, 0xe5, 0xba, 0x4d, 0xa5, 0xd1, 0xbb, 0x5e, 0xe3, 0xc7, 0x27, 0x0c, 0x57,
440 0x76, 0xd4, 0x2f, 0xb6, 0x5c, 0x21, 0xb7, 0x3a
441 ]
442 );
443 }
444
445 #[test]
446 fn test_hmac_sha1() {
447 let hmac = hmac_sha1(b"That's a secret".as_slice(), b"Hello world!".as_slice()).unwrap();
448
449 assert_eq!(
450 hmac,
451 [
452 0xe1, 0x06, 0x76, 0x46, 0x3b, 0x82, 0x67, 0xa1, 0xae, 0xe5, 0x1c, 0xfa, 0xee, 0x36,
453 0x1d, 0x4b, 0xd4, 0x41, 0x6e, 0x37
454 ]
455 );
456 }
457
458 #[test]
459 fn test_md5() {
460 for test in MD5_TESTS.iter() {
461 hash_test(MessageDigest::md5(), test);
462 }
463 }
464
465 #[test]
466 fn test_md5_recycle() {
467 let mut h = Hasher::new(MessageDigest::md5()).unwrap();
468 for test in MD5_TESTS.iter() {
469 hash_recycle_test(&mut h, test);
470 }
471 }
472
473 #[test]
474 fn test_finish_twice() {
475 let mut h = Hasher::new(MessageDigest::md5()).unwrap();
476 h.write_all(&Vec::from_hex(MD5_TESTS[6].0).unwrap())
477 .unwrap();
478 h.finish().unwrap();
479 let res = h.finish().unwrap();
480 let null = hash(MessageDigest::md5(), &[]).unwrap();
481 assert_eq!(&*res, &*null);
482 }
483
484 #[test]
485 #[allow(clippy::redundant_clone)]
486 fn test_clone() {
487 let i = 7;
488 let inp = Vec::from_hex(MD5_TESTS[i].0).unwrap();
489 assert!(inp.len() > 2);
490 let p = inp.len() / 2;
491 let h0 = Hasher::new(MessageDigest::md5()).unwrap();
492
493 println!("Clone a new hasher");
494 let mut h1 = h0.clone();
495 h1.write_all(&inp[..p]).unwrap();
496 {
497 println!("Clone an updated hasher");
498 let mut h2 = h1.clone();
499 h2.write_all(&inp[p..]).unwrap();
500 let res = h2.finish().unwrap();
501 assert_eq!(hex::encode(res), MD5_TESTS[i].1);
502 }
503 h1.write_all(&inp[p..]).unwrap();
504 let res = h1.finish().unwrap();
505 assert_eq!(hex::encode(res), MD5_TESTS[i].1);
506
507 println!("Clone a finished hasher");
508 let mut h3 = h1.clone();
509 h3.write_all(&Vec::from_hex(MD5_TESTS[i + 1].0).unwrap())
510 .unwrap();
511 let res = h3.finish().unwrap();
512 assert_eq!(hex::encode(res), MD5_TESTS[i + 1].1);
513 }
514
515 #[test]
516 fn test_sha1() {
517 let tests = [("616263", "a9993e364706816aba3e25717850c26c9cd0d89d")];
518
519 for test in tests.iter() {
520 hash_test(MessageDigest::sha1(), test);
521 }
522 }
523
524 #[test]
525 fn test_sha224() {
526 let tests = [(
527 "616263",
528 "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
529 )];
530
531 for test in tests.iter() {
532 hash_test(MessageDigest::sha224(), test);
533 }
534 }
535
536 #[test]
537 fn test_sha256() {
538 let tests = [(
539 "616263",
540 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
541 )];
542
543 for test in tests.iter() {
544 hash_test(MessageDigest::sha256(), test);
545 }
546 }
547
548 #[test]
549 fn test_sha512() {
550 let tests = [(
551 "616263",
552 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2\
553 192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
554 )];
555
556 for test in tests.iter() {
557 hash_test(MessageDigest::sha512(), test);
558 }
559 }
560
561 #[test]
562 fn test_sha512_256() {
563 let tests = [(
564 "616263",
565 "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23",
566 )];
567
568 for test in tests.iter() {
569 hash_test(MessageDigest::sha512_256(), test);
570 }
571 }
572
573 #[test]
574 fn from_nid() {
575 assert_eq!(
576 MessageDigest::from_nid(Nid::SHA256).unwrap().as_ptr(),
577 MessageDigest::sha256().as_ptr()
578 );
579 }
580}