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