Files
wolfssl/wrapper/rust/wolfssl-wolfcrypt/src/hmac.rs
T
2026-05-18 12:27:45 -07:00

364 lines
12 KiB
Rust

/*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/*!
This module provides a Rust wrapper for the wolfCrypt library's HMAC
functionality.
*/
#![cfg(hmac)]
use crate::sys;
use core::mem::MaybeUninit;
/// Rust wrapper for wolfSSL `Hmac` object.
pub struct HMAC {
wc_hmac: sys::Hmac,
}
impl HMAC {
pub const TYPE_MD5: i32 = sys::wc_HashType_WC_HASH_TYPE_MD5 as i32;
pub const TYPE_SHA: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA as i32;
pub const TYPE_SHA256: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA256 as i32;
pub const TYPE_SHA512: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA512 as i32;
#[cfg(sha512_224)]
pub const TYPE_SHA512_224: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA512_224 as i32;
#[cfg(sha512_256)]
pub const TYPE_SHA512_256: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA512_256 as i32;
pub const TYPE_SHA384: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA384 as i32;
pub const TYPE_SHA224: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA224 as i32;
pub const TYPE_SHA3_224: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA3_224 as i32;
pub const TYPE_SHA3_256: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA3_256 as i32;
pub const TYPE_SHA3_384: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA3_384 as i32;
pub const TYPE_SHA3_512: i32 = sys::wc_HashType_WC_HASH_TYPE_SHA3_512 as i32;
/// Get HMAC hash size by type.
///
/// # Parameters
///
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
///
/// # Returns
///
/// Returns either Ok(size) containing the HMAC hash size or Err(e)
/// containing the wolfSSL library error code value.
pub fn get_hmac_size_by_type(typ: i32) -> Result<usize, i32> {
let rc = unsafe { sys::wc_HmacSizeByType(typ) };
if rc < 0 {
return Err(rc);
}
Ok(rc as u32 as usize)
}
/// Create a new HMAC object with the given hash type and encryption key.
///
/// # Parameters
///
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
/// * `key`: Encryption key.
///
/// # Returns
///
/// Returns either Ok(hmac) containing the HMAC struct instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 16];
/// let mut hmac = HMAC::new(HMAC::TYPE_SHA256, &key).expect("Error with new()");
/// ```
pub fn new(typ: i32, key: &[u8]) -> Result<Self, i32> {
Self::new_ex(typ, key, None, None)
}
/// Create a new HMAC object with the given hash type and encryption key
/// with optional heap and device ID.
///
/// # Parameters
///
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
/// * `key`: Encryption key.
/// * `heap`: Optional heap hint.
/// * `dev_id` Optional device ID to use with crypto callbacks or async hardware.
///
/// # Returns
///
/// Returns either Ok(hmac) containing the HMAC struct instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 16];
/// let mut hmac = HMAC::new_ex(HMAC::TYPE_SHA256, &key, None, None).expect("Error with new_ex()");
/// ```
pub fn new_ex(typ: i32, key: &[u8], heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>) -> Result<Self, i32> {
let key_size = crate::buffer_len_to_u32(key.len())?;
let mut wc_hmac: MaybeUninit<sys::Hmac> = MaybeUninit::uninit();
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe {
sys::wc_HmacInit(wc_hmac.as_mut_ptr(), heap, dev_id)
};
if rc != 0 {
return Err(rc);
}
let wc_hmac = unsafe { wc_hmac.assume_init() };
let mut hmac = HMAC { wc_hmac };
let rc = unsafe {
sys::wc_HmacSetKey(&mut hmac.wc_hmac, typ, key.as_ptr(), key_size)
};
if rc != 0 {
return Err(rc);
}
Ok(hmac)
}
/// Create a new HMAC object with the given hash type and encryption key,
/// allowing for short encryption keys (< 112 bits) to be used.
///
/// # Parameters
///
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
/// * `key`: Encryption key.
///
/// # Returns
///
/// Returns either Ok(hmac) containing the HMAC struct instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #![cfg(hmac_setkey_ex)]
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 3];
/// let mut hmac = HMAC::new_allow_short_key(HMAC::TYPE_SHA256, &key).expect("Error with new_allow_short_key()");
/// ```
#[cfg(hmac_setkey_ex)]
pub fn new_allow_short_key(typ: i32, key: &[u8]) -> Result<Self, i32> {
Self::new_allow_short_key_ex(typ, key, None, None)
}
/// Create a new HMAC object with the given hash type and encryption key,
/// allowing for short encryption keys (< 112 bits) to be used.
///
/// # Parameters
///
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
/// * `key`: Encryption key.
/// * `heap`: Optional heap hint.
/// * `dev_id` Optional device ID to use with crypto callbacks or async hardware.
///
/// # Returns
///
/// Returns either Ok(hmac) containing the HMAC struct instance or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// #![cfg(hmac_setkey_ex)]
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 3];
/// let mut hmac = HMAC::new_allow_short_key_ex(HMAC::TYPE_SHA256, &key, None, None).expect("Error with new_allow_short_key_ex()");
/// ```
#[cfg(hmac_setkey_ex)]
pub fn new_allow_short_key_ex(typ: i32, key: &[u8], heap: Option<*mut core::ffi::c_void>, dev_id: Option<i32>) -> Result<Self, i32> {
let key_size = crate::buffer_len_to_u32(key.len())?;
let mut wc_hmac: MaybeUninit<sys::Hmac> = MaybeUninit::uninit();
let heap = match heap {
Some(heap) => heap,
None => core::ptr::null_mut(),
};
let dev_id = match dev_id {
Some(dev_id) => dev_id,
None => sys::INVALID_DEVID,
};
let rc = unsafe {
sys::wc_HmacInit(wc_hmac.as_mut_ptr(), heap, dev_id)
};
if rc != 0 {
return Err(rc);
}
let wc_hmac = unsafe { wc_hmac.assume_init() };
let mut hmac = HMAC { wc_hmac };
let rc = unsafe {
sys::wc_HmacSetKey_ex(&mut hmac.wc_hmac, typ, key.as_ptr(), key_size, 1)
};
if rc != 0 {
return Err(rc);
}
Ok(hmac)
}
/// Update the message to authenticate using HMAC.
///
/// This function may be called multiple times to update the message to
/// hash.
///
/// # Parameters
///
/// * `data`: Buffer containing message data to append.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 16];
/// let mut hmac = HMAC::new(HMAC::TYPE_SHA256, &key).expect("Error with new()");
/// hmac.update(b"input").expect("Error with update()");
/// ```
pub fn update(&mut self, data: &[u8]) -> Result<(), i32> {
let data_size = crate::buffer_len_to_u32(data.len())?;
let rc = unsafe {
sys::wc_HmacUpdate(&mut self.wc_hmac, data.as_ptr(), data_size)
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Compute the final hash of the input message.
///
/// # Parameters
///
/// * `hash`: Output buffer to contain the calculated hash. The length must
/// match that returned by `get_hmac_size()`.
///
/// # Returns
///
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
/// library error code value.
///
/// # Example
///
/// ```rust
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 16];
/// let mut hmac = HMAC::new(HMAC::TYPE_SHA256, &key).expect("Error with new()");
/// hmac.update(b"input").expect("Error with update()");
/// let hash_size = hmac.get_hmac_size().expect("Error with get_hmac_size()");
/// let mut hash = vec![0u8; hash_size];
/// hmac.finalize(&mut hash).expect("Error with finalize()");
/// ```
pub fn finalize(&mut self, hash: &mut [u8]) -> Result<(), i32> {
// Check the output buffer size since wc_HmacFinal() does not accept
// a length parameter.
let typ = self.wc_hmac.macType as i32;
let rc = unsafe { sys::wc_HmacSizeByType(typ) };
if rc < 0 {
return Err(rc);
}
let expected_size = rc as usize;
if hash.len() != expected_size {
return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E);
}
let rc = unsafe {
sys::wc_HmacFinal(&mut self.wc_hmac, hash.as_mut_ptr())
};
if rc != 0 {
return Err(rc);
}
Ok(())
}
/// Get the HMAC hash size.
///
/// # Returns
///
/// Returns either Ok(size) containing the HMAC hash size or Err(e)
/// containing the wolfSSL library error code value.
///
/// # Example
///
/// ```rust
/// use wolfssl_wolfcrypt::hmac::HMAC;
/// let key = [0x42u8; 16];
/// let mut hmac = HMAC::new(HMAC::TYPE_SHA256, &key).expect("Error with new()");
/// hmac.update(b"input").expect("Error with update()");
/// let hash_size = hmac.get_hmac_size().expect("Error with get_hmac_size()");
/// let mut hash = vec![0u8; hash_size];
/// hmac.finalize(&mut hash).expect("Error with finalize()");
/// ```
pub fn get_hmac_size(&self) -> Result<usize, i32> {
let typ = self.wc_hmac.macType as u32 as i32;
let rc = unsafe { sys::wc_HmacSizeByType(typ) };
if rc < 0 {
return Err(rc);
}
let expected_size = rc as u32 as usize;
Ok(expected_size)
}
}
impl HMAC {
fn zeroize(&mut self) {
unsafe { crate::zeroize_raw(&mut self.wc_hmac); }
}
}
impl Clone for HMAC {
/// Deep-copy the HMAC state via `wc_HmacCopy()`.
///
/// Panics if the underlying wolfSSL copy fails.
fn clone(&self) -> Self {
let mut wc_hmac: MaybeUninit<sys::Hmac> = MaybeUninit::uninit();
let rc = unsafe {
sys::wc_HmacCopy(&self.wc_hmac as *const _ as *mut _,
wc_hmac.as_mut_ptr())
};
if rc != 0 {
panic!("wc_HmacCopy() failed: {}", rc);
}
let wc_hmac = unsafe { wc_hmac.assume_init() };
HMAC { wc_hmac }
}
}
impl Drop for HMAC {
/// Safely free the underlying wolfSSL Hmac context.
///
/// This calls the `wc_HmacFree()` wolfssl library function.
///
/// The Rust Drop trait guarantees that this method is called when the
/// HMAC struct instance goes out of scope, automatically cleaning up
/// resources and preventing memory leaks.
fn drop(&mut self) {
unsafe { sys::wc_HmacFree(&mut self.wc_hmac); }
self.zeroize();
}
}