| diff --git a/src/hmac.rs b/src/hmac.rs |
| index 601ae01..465781e 100644 |
| --- a/src/hmac.rs |
| +++ b/src/hmac.rs |
| @@ -1,10 +1,12 @@ |
| -use crate::cvt_p; |
| use crate::error::ErrorStack; |
| use crate::md::MdRef; |
| +use crate::{cvt, cvt_p}; |
| +use ffi::HMAC_CTX; |
| use foreign_types::ForeignTypeRef; |
| +use libc::{c_uint, c_void}; |
| use openssl_macros::corresponds; |
| -use libc::{c_void, c_uint}; |
| use std::convert::TryFrom; |
| +use std::ptr; |
| |
| /// Computes the HMAC as a one-shot operation. |
| /// |
| @@ -20,8 +22,9 @@ pub fn hmac<'a>( |
| md: &MdRef, |
| key: &[u8], |
| data: &[u8], |
| - out: &'a mut [u8] |
| + out: &'a mut [u8], |
| ) -> Result<&'a [u8], ErrorStack> { |
| + assert!(out.len() >= md.size()); |
| let mut out_len = c_uint::try_from(out.len()).unwrap(); |
| unsafe { |
| cvt_p(ffi::HMAC( |
| @@ -31,38 +34,184 @@ pub fn hmac<'a>( |
| data.as_ptr(), |
| data.len(), |
| out.as_mut_ptr(), |
| - &mut out_len |
| - ))?; |
| + &mut out_len, |
| + ))?; |
| } |
| Ok(&out[..out_len as usize]) |
| } |
| |
| +/// A context object used to perform HMAC operations. |
| +/// |
| +/// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message |
| +/// authentication, which is based on a hash function. |
| +/// |
| +/// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead. |
| +#[cfg(boringssl)] |
| +pub struct HmacCtx { |
| + ctx: *mut HMAC_CTX, |
| + output_size: usize, |
| +} |
| + |
| +#[cfg(boringssl)] |
| +impl HmacCtx { |
| + /// Creates a new [HmacCtx] to use the hash function `md` and key `key`. |
| + #[corresponds(HMAC_Init_ex)] |
| + pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> { |
| + unsafe { |
| + // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into |
| + // ErrorStack in the returned result by `cvt_p`. |
| + let ctx = cvt_p(ffi::HMAC_CTX_new())?; |
| + // Safety: |
| + // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new, |
| + // which is the line above. |
| + // - HMAC_Init_ex may return an error if key is null but the md is different from |
| + // before. This is avoided here since key is guaranteed to be non-null. |
| + cvt(ffi::HMAC_Init_ex( |
| + ctx, |
| + key.as_ptr() as *const c_void, |
| + key.len(), |
| + md.as_ptr(), |
| + ptr::null_mut(), |
| + ))?; |
| + Ok(Self { |
| + ctx, |
| + output_size: md.size(), |
| + }) |
| + } |
| + } |
| + |
| + /// `update` can be called repeatedly with chunks of the message `data` to be authenticated. |
| + #[corresponds(HMAC_Update)] |
| + pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { |
| + unsafe { |
| + // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the |
| + // returned result by `cvt`. |
| + cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ()) |
| + } |
| + } |
| + |
| + /// Finishes the HMAC process, and places the message authentication code in `output`. |
| + /// The number of bytes written to `output` is returned. |
| + /// |
| + /// # Panics |
| + /// |
| + /// Panics if the `output` is smaller than the required size. The output size is indicated by |
| + /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will |
| + /// always be large enough. |
| + #[corresponds(HMAC_Final)] |
| + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { |
| + assert!(output.len() >= self.output_size); |
| + unsafe { |
| + // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer |
| + // than the length of `output`. |
| + let mut size: c_uint = 0; |
| + cvt(ffi::HMAC_Final( |
| + self.ctx, |
| + output.as_mut_ptr(), |
| + &mut size as *mut c_uint, |
| + )) |
| + .map(|_| size as usize) |
| + } |
| + } |
| +} |
| + |
| +impl Drop for HmacCtx { |
| + #[corresponds(HMAC_CTX_free)] |
| + fn drop(&mut self) { |
| + unsafe { |
| + ffi::HMAC_CTX_free(self.ctx); |
| + } |
| + } |
| +} |
| + |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::md::Md; |
| - use crate::memcmp; |
| |
| - const SHA_256_DIGEST_SIZE:usize = 32; |
| + const SHA_256_DIGEST_SIZE: usize = 32; |
| |
| #[test] |
| fn hmac_sha256_test() { |
| - let expected_hmac = [0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7]; |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| - let key:[u8; 20] = [0x0b; 20]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| let data = b"Hi There"; |
| - let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| - expect!(memcmp::eq(&hmac_result, &expected_hmac)); |
| + let hmac_result = |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| + assert_eq!(&hmac_result, &expected_hmac); |
| + } |
| + |
| + #[test] |
| + #[should_panic] |
| + fn hmac_sha256_output_too_short() { |
| + let mut out = vec![0_u8; 1]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| } |
| |
| #[test] |
| fn hmac_sha256_test_big_buffer() { |
| - let expected_hmac = [0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7]; |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| let mut out: [u8; 100] = [0; 100]; |
| - let key:[u8;20] = [0x0b; 20]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + let hmac_result = |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| + assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); |
| + assert_eq!(&hmac_result, &expected_hmac); |
| + } |
| + |
| + #[test] |
| + fn hmac_sha256_update_test() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| let data = b"Hi There"; |
| - let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| - expect_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); |
| - expect!(memcmp::eq(&hmac_result, &expected_hmac)); |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(data).unwrap(); |
| + let size = hmac_ctx.finalize(&mut out).unwrap(); |
| + assert_eq!(&out, &expected_hmac); |
| + assert_eq!(size, SHA_256_DIGEST_SIZE); |
| + } |
| + |
| + #[test] |
| + fn hmac_sha256_update_chunks_test() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(b"Hi").unwrap(); |
| + hmac_ctx.update(b" There").unwrap(); |
| + let size = hmac_ctx.finalize(&mut out).unwrap(); |
| + assert_eq!(&out, &expected_hmac); |
| + assert_eq!(size, SHA_256_DIGEST_SIZE); |
| + } |
| + |
| + #[test] |
| + #[should_panic] |
| + fn hmac_sha256_update_output_too_short() { |
| + let mut out = vec![0_u8; 1]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(b"Hi There").unwrap(); |
| + hmac_ctx.finalize(&mut out).unwrap(); |
| } |
| } |