boring_imp/
hash.rs

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    /// Creates a `MessageDigest` from a raw OpenSSL pointer.
20    ///
21    /// # Safety
22    ///
23    /// The caller must ensure the pointer is valid.
24    pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self {
25        MessageDigest(x)
26    }
27
28    /// Returns the `MessageDigest` corresponding to an `Nid`.
29    ///
30    /// This corresponds to [`EVP_get_digestbynid`].
31    ///
32    /// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestInit.html
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    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    /// The size of the digest in bytes.
78    #[allow(clippy::trivially_copy_pass_by_ref)]
79    pub fn size(&self) -> usize {
80        unsafe { ffi::EVP_MD_size(self.0) }
81    }
82
83    /// The name of the digest.
84    #[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
102/// Provides message digest (hash) computation.
103///
104/// # Examples
105///
106/// Calculate a hash in one go:
107///
108/// ```
109/// use boring::hash::{hash, MessageDigest};
110///
111/// let data = b"\x42\xF4\x97\xE0";
112/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2";
113/// let res = hash(MessageDigest::md5(), data).unwrap();
114/// assert_eq!(&*res, spec);
115/// ```
116///
117/// Supply the input in chunks:
118///
119/// ```
120/// use boring::hash::{Hasher, MessageDigest};
121///
122/// let data = [b"\x42\xF4", b"\x97\xE0"];
123/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2";
124/// let mut h = Hasher::new(MessageDigest::md5()).unwrap();
125/// h.update(data[0]).unwrap();
126/// h.update(data[1]).unwrap();
127/// let res = h.finish().unwrap();
128/// assert_eq!(&*res, spec);
129/// ```
130///
131/// # Warning
132///
133/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore.
134///
135/// Don't ever hash passwords, use the functions in the `pkcs5` module or bcrypt/scrypt instead.
136///
137/// For extendable output functions (XOFs, i.e. SHAKE128/SHAKE256), you must use finish_xof instead
138/// of finish and provide a buf to store the hash. The hash will be as long as the buf.
139pub 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    /// Creates a new `Hasher` with the specified hash type.
151    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    /// Feeds data into the hasher.
182    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    /// Returns the hash of the data written and resets the non-XOF hasher.
198    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    /// Writes the hash of the data into the supplied buf and resets the XOF hasher.
219    /// The hash will be as long as the buf.
220    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/// The resulting bytes of a digest.
278///
279/// This type derefs to a byte slice - it exists to avoid allocating memory to
280/// store the digest data.
281#[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
323/// Computes the hash of the `data` with the non-XOF hasher `t`.
324pub 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
330/// Computes the hash of the `data` with the XOF hasher `t` and stores it in `buf`.
331pub 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
337/// Computes HMAC with SHA-256 digest.
338pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; 32], ErrorStack> {
339    hmac(MessageDigest::sha256(), key, data)
340}
341
342/// Computes HMAC with SHA-512 digest.
343pub fn hmac_sha512(key: &[u8], data: &[u8]) -> Result<[u8; 64], ErrorStack> {
344    hmac(MessageDigest::sha512(), key, data)
345}
346
347/// Computes HMAC with SHA-1 digest.
348pub 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    // Test vectors from http://www.nsrl.nist.gov/testdata/
396    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}