Skip to main content

boring/
hmac.rs

1use crate::cvt;
2use crate::error::ErrorStack;
3use crate::foreign_types::ForeignTypeRef;
4use crate::hash::MessageDigest;
5use openssl_macros::corresponds;
6
7foreign_type_and_impl_send_sync! {
8    type CType = ffi::HMAC_CTX;
9    fn drop = ffi::HMAC_CTX_free;
10
11    pub struct HmacCtx;
12}
13
14impl HmacCtxRef {
15    /// Configures HmacCtx to use `md` as the hash function and `key` as the key.
16    ///
17    #[corresponds(HMAC_Init_ex)]
18    pub fn init(&mut self, key: &[u8], md: &MessageDigest) -> Result<(), ErrorStack> {
19        ffi::init();
20
21        unsafe {
22            cvt(ffi::HMAC_Init_ex(
23                self.as_ptr(),
24                key.as_ptr().cast(),
25                key.len(),
26                md.as_ptr(),
27                // ENGINE api is deprecated
28                core::ptr::null_mut(),
29            ))
30        }
31    }
32}
33
34/// Provides an init-update-finalize API for HMAC.
35pub struct Hmac(*mut ffi::HMAC_CTX);
36
37impl Hmac {
38    /// Creates a new HMAC object with the given key and hash algorithm.
39    pub fn init(key: &[u8], md: &MessageDigest) -> Result<Hmac, ErrorStack> {
40        ffi::init();
41
42        let ctx = unsafe {
43            let ctx = ffi::HMAC_CTX_new();
44            cvt(ffi::HMAC_Init_ex(
45                ctx,
46                key.as_ptr().cast(),
47                key.len(),
48                md.as_ptr(),
49                // ENGINE api is deprecated
50                core::ptr::null_mut(),
51            ))?;
52            ctx
53        };
54
55        Ok(Hmac(ctx))
56    }
57
58    /// Updates the HMAC input.
59    pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
60        unsafe { cvt(ffi::HMAC_Update(self.0, data.as_ptr().cast(), data.len())) }
61    }
62
63    /// Finalizes the HMAC and returns the output.
64    pub fn finalize(self) -> Result<Vec<u8>, ErrorStack> {
65        let out_len = unsafe { ffi::HMAC_size(self.0) };
66        let mut out = vec![0; out_len];
67        unsafe {
68            cvt(ffi::HMAC_Final(
69                self.0,
70                out.as_mut_ptr().cast(),
71                // ENGINE api is deprecated
72                core::ptr::null_mut(),
73            ))?;
74        }
75        Ok(out)
76    }
77}
78
79impl Drop for Hmac {
80    fn drop(&mut self) {
81        unsafe { ffi::HMAC_CTX_free(self.0) }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use crate::hash;
88
89    use super::*;
90
91    fn test<const N: usize>(md: MessageDigest) {
92        assert_eq!(N, md.size());
93        let key = vec![0; N];
94        let message_parts = [
95            b"hello".to_vec(),
96            b"world!".to_vec(),
97            b"".to_vec(),
98            vec![0; 23],
99            b"fella guy".to_vec(),
100        ];
101        let message = message_parts.concat();
102
103        let mut hmac = Hmac::init(&key, &md).unwrap();
104        for part in &message_parts {
105            hmac.update(part).unwrap();
106        }
107        let res = hmac.finalize().unwrap();
108        assert_eq!(res, hash::hmac::<N>(md, &key, &message).unwrap());
109    }
110
111    #[test]
112    fn test_sha1() {
113        test::<20>(MessageDigest::sha1());
114    }
115
116    #[test]
117    fn test_sha256() {
118        test::<32>(MessageDigest::sha256());
119    }
120
121    #[test]
122    fn test_sha384() {
123        test::<48>(MessageDigest::sha384());
124    }
125
126    #[test]
127    fn test_sha512() {
128        test::<64>(MessageDigest::sha512());
129    }
130}