1 use crate::error::ErrorStack;
2 use crate::md::MdRef;
3 use crate::{cvt, cvt_p};
4 use ffi::HMAC_CTX;
5 use foreign_types::ForeignTypeRef;
6 use libc::{c_uint, c_void};
7 use openssl_macros::corresponds;
8 use std::convert::TryFrom;
9 use std::ptr;
10 
11 /// Computes the HMAC as a one-shot operation.
12 ///
13 /// Calculates the HMAC of data, using the given |key|
14 /// and hash function |md|, and returns the result re-using the space from
15 /// buffer |out|. On entry, |out| must contain at least |EVP_MD_size| bytes
16 /// of space. The actual length of the result is used to resize the returned
17 /// slice. An output size of |EVP_MAX_MD_SIZE| will always be large enough.
18 /// It returns a resized |out| or ErrorStack on error.
19 #[corresponds(HMAC)]
20 #[inline]
hmac<'a>( md: &MdRef, key: &[u8], data: &[u8], out: &'a mut [u8], ) -> Result<&'a [u8], ErrorStack>21 pub fn hmac<'a>(
22     md: &MdRef,
23     key: &[u8],
24     data: &[u8],
25     out: &'a mut [u8],
26 ) -> Result<&'a [u8], ErrorStack> {
27     assert!(out.len() >= md.size());
28     let mut out_len = c_uint::try_from(out.len()).unwrap();
29     unsafe {
30         cvt_p(ffi::HMAC(
31             md.as_ptr(),
32             key.as_ptr() as *const c_void,
33             key.len(),
34             data.as_ptr(),
35             data.len(),
36             out.as_mut_ptr(),
37             &mut out_len,
38         ))?;
39     }
40     Ok(&out[..out_len as usize])
41 }
42 
43 /// A context object used to perform HMAC operations.
44 ///
45 /// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message
46 /// authentication, which is based on a hash function.
47 ///
48 /// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead.
49 #[cfg(boringssl)]
50 pub struct HmacCtx {
51     ctx: *mut HMAC_CTX,
52     output_size: usize,
53 }
54 
55 #[cfg(boringssl)]
56 impl HmacCtx {
57     /// Creates a new [HmacCtx] to use the hash function `md` and key `key`.
58     #[corresponds(HMAC_Init_ex)]
new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack>59     pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> {
60         unsafe {
61             // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into
62             // ErrorStack in the returned result by `cvt_p`.
63             let ctx = cvt_p(ffi::HMAC_CTX_new())?;
64             // Safety:
65             // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
66             //   which is the line above.
67             // - HMAC_Init_ex may return an error if key is null but the md is different from
68             //   before. This is avoided here since key is guaranteed to be non-null.
69             cvt(ffi::HMAC_Init_ex(
70                 ctx,
71                 key.as_ptr() as *const c_void,
72                 key.len(),
73                 md.as_ptr(),
74                 ptr::null_mut(),
75             ))?;
76             Ok(Self {
77                 ctx,
78                 output_size: md.size(),
79             })
80         }
81     }
82 
83     /// `update` can be called repeatedly with chunks of the message `data` to be authenticated.
84     #[corresponds(HMAC_Update)]
update(&mut self, data: &[u8]) -> Result<(), ErrorStack>85     pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
86         unsafe {
87             // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the
88             // returned result by `cvt`.
89             cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ())
90         }
91     }
92 
93     /// Finishes the HMAC process, and places the message authentication code in `output`.
94     /// The number of bytes written to `output` is returned.
95     ///
96     /// # Panics
97     ///
98     /// Panics if the `output` is smaller than the required size. The output size is indicated by
99     /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will
100     /// always be large enough.
101     #[corresponds(HMAC_Final)]
finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack>102     pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
103         assert!(output.len() >= self.output_size);
104         unsafe {
105             // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer
106             // than the length of `output`.
107             let mut size: c_uint = 0;
108             cvt(ffi::HMAC_Final(
109                 self.ctx,
110                 output.as_mut_ptr(),
111                 &mut size as *mut c_uint,
112             ))
113             .map(|_| size as usize)
114         }
115     }
116 }
117 
118 impl Drop for HmacCtx {
119     #[corresponds(HMAC_CTX_free)]
drop(&mut self)120     fn drop(&mut self) {
121         unsafe {
122             ffi::HMAC_CTX_free(self.ctx);
123         }
124     }
125 }
126 
127 #[cfg(test)]
128 mod tests {
129     use super::*;
130     use crate::md::Md;
131 
132     const SHA_256_DIGEST_SIZE: usize = 32;
133 
134     #[test]
hmac_sha256_test()135     fn hmac_sha256_test() {
136         let expected_hmac = [
137             0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
138             0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
139             0x2e, 0x32, 0xcf, 0xf7,
140         ];
141         let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
142         let key: [u8; 20] = [0x0b; 20];
143         let data = b"Hi There";
144         let hmac_result =
145             hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
146         assert_eq!(&hmac_result, &expected_hmac);
147     }
148 
149     #[test]
150     #[should_panic]
hmac_sha256_output_too_short()151     fn hmac_sha256_output_too_short() {
152         let mut out = vec![0_u8; 1];
153         let key: [u8; 20] = [0x0b; 20];
154         let data = b"Hi There";
155         hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
156     }
157 
158     #[test]
hmac_sha256_test_big_buffer()159     fn hmac_sha256_test_big_buffer() {
160         let expected_hmac = [
161             0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
162             0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
163             0x2e, 0x32, 0xcf, 0xf7,
164         ];
165         let mut out: [u8; 100] = [0; 100];
166         let key: [u8; 20] = [0x0b; 20];
167         let data = b"Hi There";
168         let hmac_result =
169             hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
170         assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
171         assert_eq!(&hmac_result, &expected_hmac);
172     }
173 
174     #[test]
hmac_sha256_update_test()175     fn hmac_sha256_update_test() {
176         let expected_hmac = [
177             0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
178             0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
179             0x2e, 0x32, 0xcf, 0xf7,
180         ];
181         let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
182         let key: [u8; 20] = [0x0b; 20];
183         let data = b"Hi There";
184         let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
185         hmac_ctx.update(data).unwrap();
186         let size = hmac_ctx.finalize(&mut out).unwrap();
187         assert_eq!(&out, &expected_hmac);
188         assert_eq!(size, SHA_256_DIGEST_SIZE);
189     }
190 
191     #[test]
hmac_sha256_update_chunks_test()192     fn hmac_sha256_update_chunks_test() {
193         let expected_hmac = [
194             0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
195             0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
196             0x2e, 0x32, 0xcf, 0xf7,
197         ];
198         let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
199         let key: [u8; 20] = [0x0b; 20];
200         let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
201         hmac_ctx.update(b"Hi").unwrap();
202         hmac_ctx.update(b" There").unwrap();
203         let size = hmac_ctx.finalize(&mut out).unwrap();
204         assert_eq!(&out, &expected_hmac);
205         assert_eq!(size, SHA_256_DIGEST_SIZE);
206     }
207 
208     #[test]
209     #[should_panic]
hmac_sha256_update_output_too_short()210     fn hmac_sha256_update_output_too_short() {
211         let mut out = vec![0_u8; 1];
212         let key: [u8; 20] = [0x0b; 20];
213         let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
214         hmac_ctx.update(b"Hi There").unwrap();
215         hmac_ctx.finalize(&mut out).unwrap();
216     }
217 }
218