1diff --git a/src/hmac.rs b/src/hmac.rs 2index 601ae01..465781e 100644 3--- a/src/hmac.rs 4+++ b/src/hmac.rs 5@@ -1,10 +1,12 @@ 6-use crate::cvt_p; 7 use crate::error::ErrorStack; 8 use crate::md::MdRef; 9+use crate::{cvt, cvt_p}; 10+use ffi::HMAC_CTX; 11 use foreign_types::ForeignTypeRef; 12+use libc::{c_uint, c_void}; 13 use openssl_macros::corresponds; 14-use libc::{c_void, c_uint}; 15 use std::convert::TryFrom; 16+use std::ptr; 17 18 /// Computes the HMAC as a one-shot operation. 19 /// 20@@ -20,8 +22,9 @@ pub fn hmac<'a>( 21 md: &MdRef, 22 key: &[u8], 23 data: &[u8], 24- out: &'a mut [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@@ -31,38 +34,184 @@ pub fn hmac<'a>( 32 data.as_ptr(), 33 data.len(), 34 out.as_mut_ptr(), 35- &mut out_len 36- ))?; 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)] 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)] 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)] 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)] 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- use crate::memcmp; 132 133- const SHA_256_DIGEST_SIZE:usize = 32; 134+ const SHA_256_DIGEST_SIZE: usize = 32; 135 136 #[test] 137 fn hmac_sha256_test() { 138- 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]; 139+ let expected_hmac = [ 140+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 141+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 142+ 0x2e, 0x32, 0xcf, 0xf7, 143+ ]; 144 let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; 145- let key:[u8; 20] = [0x0b; 20]; 146+ let key: [u8; 20] = [0x0b; 20]; 147 let data = b"Hi There"; 148- let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); 149- expect!(memcmp::eq(&hmac_result, &expected_hmac)); 150+ let hmac_result = 151+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); 152+ assert_eq!(&hmac_result, &expected_hmac); 153+ } 154+ 155+ #[test] 156+ #[should_panic] 157+ fn hmac_sha256_output_too_short() { 158+ let mut out = vec![0_u8; 1]; 159+ let key: [u8; 20] = [0x0b; 20]; 160+ let data = b"Hi There"; 161+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); 162 } 163 164 #[test] 165 fn hmac_sha256_test_big_buffer() { 166- 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]; 167+ let expected_hmac = [ 168+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 169+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 170+ 0x2e, 0x32, 0xcf, 0xf7, 171+ ]; 172 let mut out: [u8; 100] = [0; 100]; 173- let key:[u8;20] = [0x0b; 20]; 174+ let key: [u8; 20] = [0x0b; 20]; 175+ let data = b"Hi There"; 176+ let hmac_result = 177+ hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); 178+ assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); 179+ assert_eq!(&hmac_result, &expected_hmac); 180+ } 181+ 182+ #[test] 183+ fn hmac_sha256_update_test() { 184+ let expected_hmac = [ 185+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 186+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 187+ 0x2e, 0x32, 0xcf, 0xf7, 188+ ]; 189+ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; 190+ let key: [u8; 20] = [0x0b; 20]; 191 let data = b"Hi There"; 192- let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); 193- expect_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); 194- expect!(memcmp::eq(&hmac_result, &expected_hmac)); 195+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); 196+ hmac_ctx.update(data).unwrap(); 197+ let size = hmac_ctx.finalize(&mut out).unwrap(); 198+ assert_eq!(&out, &expected_hmac); 199+ assert_eq!(size, SHA_256_DIGEST_SIZE); 200+ } 201+ 202+ #[test] 203+ fn hmac_sha256_update_chunks_test() { 204+ let expected_hmac = [ 205+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 206+ 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 207+ 0x2e, 0x32, 0xcf, 0xf7, 208+ ]; 209+ let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; 210+ let key: [u8; 20] = [0x0b; 20]; 211+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); 212+ hmac_ctx.update(b"Hi").unwrap(); 213+ hmac_ctx.update(b" There").unwrap(); 214+ let size = hmac_ctx.finalize(&mut out).unwrap(); 215+ assert_eq!(&out, &expected_hmac); 216+ assert_eq!(size, SHA_256_DIGEST_SIZE); 217+ } 218+ 219+ #[test] 220+ #[should_panic] 221+ fn hmac_sha256_update_output_too_short() { 222+ let mut out = vec![0_u8; 1]; 223+ let key: [u8; 20] = [0x0b; 20]; 224+ let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); 225+ hmac_ctx.update(b"Hi There").unwrap(); 226+ hmac_ctx.finalize(&mut out).unwrap(); 227 } 228 } 229