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