Skip to main content

boring/
hash.rs

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    /// Creates a `MessageDigest` from a raw OpenSSL pointer.
21    ///
22    /// # Safety
23    ///
24    /// The caller must ensure the pointer is valid.
25    #[must_use]
26    pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self {
27        MessageDigest(x)
28    }
29
30    /// Returns the `MessageDigest` corresponding to an `Nid`.
31    #[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    /// The size of the digest in bytes.
86    #[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    /// The name of the digest.
93    #[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
112/// Provides message digest (hash) computation.
113///
114/// # Examples
115///
116/// Calculate a hash in one go:
117///
118/// ```
119/// use boring::hash::{hash, MessageDigest};
120///
121/// let data = b"\x42\xF4\x97\xE0";
122/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2";
123/// let res = hash(MessageDigest::md5(), data).unwrap();
124/// assert_eq!(&*res, spec);
125/// ```
126///
127/// Supply the input in chunks:
128///
129/// ```
130/// use boring::hash::{Hasher, MessageDigest};
131///
132/// let data = [b"\x42\xF4", b"\x97\xE0"];
133/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2";
134/// let mut h = Hasher::new(MessageDigest::md5()).unwrap();
135/// h.update(data[0]).unwrap();
136/// h.update(data[1]).unwrap();
137/// let res = h.finish().unwrap();
138/// assert_eq!(&*res, spec);
139/// ```
140///
141/// # Warning
142///
143/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore.
144///
145/// Don't ever hash passwords, use the functions in the `pkcs5` module or bcrypt/scrypt instead.
146///
147/// For extendable output functions (XOFs, i.e. SHAKE128/SHAKE256), you must use finish_xof instead
148/// of finish and provide a buf to store the hash. The hash will be as long as the buf.
149pub 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    /// Creates a new `Hasher` with the specified hash type.
161    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    /// Feeds data into the hasher.
192    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    /// Returns the hash of the data written and resets the non-XOF hasher.
208    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    /// Writes the hash of the data into the supplied buf and resets the XOF hasher.
229    /// The hash will be as long as the buf.
230    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/// The resulting bytes of a digest.
288///
289/// This type derefs to a byte slice - it exists to avoid allocating memory to
290/// store the digest data.
291#[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
333/// Computes the hash of the `data` with the non-XOF hasher `t`.
334pub 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
340/// Computes the hash of the `data` with the XOF hasher `t` and stores it in `buf`.
341pub 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
347/// Computes HMAC with SHA-256 digest.
348pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; 32], ErrorStack> {
349    hmac(MessageDigest::sha256(), key, data)
350}
351
352/// Computes HMAC with SHA-512 digest.
353pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; 64], ErrorStack> {
354    hmac(MessageDigest::sha512(), key, data)
355}
356
357/// Computes HMAC with SHA-1 digest.
358pub 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    // Test vectors from http://www.nsrl.nist.gov/testdata/
406    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}