pub mod algo;
mod digest;
#[doc(inline)]
pub use algo::{
Sha224, Sha256, Sha384, Sha512,
Sha3_224, Sha3_256, Sha3_384, Sha3_512,
KeySlice
};
non_fips! {
#[doc(inline)]
pub use algo::{Sha, Md5};
}
use algo::GenericKey;
use crate::{ct, Fips};
use wolf_crypto_sys::{
Hmac as wc_Hmac,
wc_HmacSetKey, wc_HmacUpdate, wc_HmacFree, wc_HmacFinal,
};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::addr_of_mut;
use crate::{can_cast_u32, const_can_cast_u32, Unspecified};
use crate::buf::InvalidSize;
use crate::mac::hmac::algo::Digest as _;
use core::fmt;
pub use digest::{Digest, HexDigest};
#[repr(transparent)]
pub struct Hmac<H: algo::Hash> {
inner: wc_Hmac,
_algo: PhantomData<H>
}
impl<H: algo::Hash> fmt::Debug for Hmac<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Hmac<")
.and_then(|_| H::write_alg_name(f))
.and_then(|_| f.write_str(">"))
}
}
impl<H: algo::Hash> Hmac<H> {
pub fn new<K>(key: K) -> Self
where K: GenericKey<Size = H::KeyLen>
{
let mut inner = MaybeUninit::<wc_Hmac>::uninit();
unsafe {
let _res = wc_HmacSetKey(
inner.as_mut_ptr(),
H::type_id(),
key.ptr(),
key.size()
);
debug_assert_eq!(_res, 0);
key.cleanup();
Self { inner: inner.assume_init(), _algo: PhantomData }
}
}
#[inline]
unsafe fn update_unchecked(&mut self, data: &[u8]) -> &mut Self {
debug_assert!(can_cast_u32(data.len()));
let _res = wc_HmacUpdate(
addr_of_mut!(self.inner),
data.as_ptr(),
data.len() as u32
);
debug_assert_eq!(_res, 0);
self
}
pub fn update(&mut self, data: &[u8]) -> Result<&mut Self, Unspecified> {
if can_cast_u32(data.len()) {
Ok(unsafe { self.update_unchecked(data) })
} else {
Err(Unspecified)
}
}
pub fn update_sized<const C: usize>(&mut self, data: &[u8; C]) -> Result<&mut Self, Unspecified> {
if const_can_cast_u32::<C>() {
Ok(unsafe { self.update_unchecked(data) })
} else {
Err(Unspecified)
}
}
#[inline(always)]
unsafe fn finalize_imp(mut self, output: *mut u8) {
debug_assert!(!output.is_null());
unsafe {
let _res = wc_HmacFinal(
addr_of_mut!(self.inner),
output
);
debug_assert_eq!(_res, 0);
}
}
#[inline]
pub fn finalize_into(self, output: &mut H::Digest) {
unsafe { self.finalize_imp(output.ptr()); }
}
pub fn finalize_into_slice(self, output: &mut [u8]) -> Result<(), InvalidSize> {
if output.len() >= <H::Digest as algo::Digest>::size() as usize {
unsafe { self.finalize_imp(output.as_mut_ptr()); }
Ok(())
} else {
Err(InvalidSize)
}
}
#[inline]
pub fn finalize(self) -> Digest<H::Digest> {
let mut out = <H::Digest as algo::Digest>::zeroes();
unsafe { self.finalize_imp(out.ptr()) };
Digest::new(out)
}
#[must_use]
pub fn compare_digest(self, other: &H::Digest) -> bool {
let finalized = self.finalize();
ct::cmp_slice(finalized.as_ref(), other.as_ref()) != 0
}
#[must_use]
pub fn compare_digest_slice(self, other: &[u8]) -> bool {
let finalized = self.finalize();
ct::cmp_slice(finalized.as_ref(), other) != 0
}
}
impl<H: algo::Hash> Drop for Hmac<H> {
#[inline]
fn drop(&mut self) {
unsafe { wc_HmacFree(addr_of_mut!(self.inner)); }
}
}
unsafe impl<H: algo::Hash> Send for Hmac<H> {}
unsafe impl<H: algo::Hash> Sync for Hmac<H> {}
impl<H: algo::Hash + Fips> crate::sealed::FipsSealed for Hmac<H> {}
impl<H: algo::Hash + Fips> Fips for Hmac<H> {}
#[cfg(test)]
mod property_tests {
use super::*;
use hmac::{Hmac as rc_Hmac, Mac as _};
use proptest::prelude::*;
use crate::aes::test_utils::{BoundList, AnyList};
proptest! {
#![proptest_config(ProptestConfig::with_cases(5_000))]
#[test]
fn rust_crypto_equivalence_sha256(
input in any::<BoundList<1024>>(),
key in any::<[u8; 32]>()
) {
let mut rc = rc_Hmac::<sha2::Sha256>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha256>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha256(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 32]>()
) {
let mut rc = rc_Hmac::<sha2::Sha256>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha256>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha224(
input in any::<BoundList<1024>>(),
key in any::<[u8; 28]>()
) {
let mut rc = rc_Hmac::<sha2::Sha224>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha224>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha224(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 28]>()
) {
let mut rc = rc_Hmac::<sha2::Sha224>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha224>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha384(
input in any::<BoundList<1024>>(),
key in any::<[u8; 48]>()
) {
let mut rc = rc_Hmac::<sha2::Sha384>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha384>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha384(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 48]>()
) {
let mut rc = rc_Hmac::<sha2::Sha384>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha384>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha512(
input in any::<BoundList<1024>>(),
key in any::<[u8; 64]>()
) {
let mut rc = rc_Hmac::<sha2::Sha512>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha512>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha512(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 64]>()
) {
let mut rc = rc_Hmac::<sha2::Sha512>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha512>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha3_256(
input in any::<BoundList<1024>>(),
key in any::<[u8; 32]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_256>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_256>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha3_256(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 32]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_256>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_256>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha3_224(
input in any::<BoundList<1024>>(),
key in any::<[u8; 28]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_224>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_224>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha3_224(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 28]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_224>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_224>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha3_384(
input in any::<BoundList<1024>>(),
key in any::<[u8; 48]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_384>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_384>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha3_384(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 48]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_384>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_384>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_sha3_512(
input in any::<BoundList<1024>>(),
key in any::<[u8; 64]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_512>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_512>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[test]
fn rust_crypto_equivalence_many_updates_sha3_512(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 64]>()
) {
let mut rc = rc_Hmac::<sha3::Sha3_512>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha3_512>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[cfg(feature = "allow-non-fips")]
#[test]
fn rust_crypto_equivalence_sha1(
input in any::<BoundList<1024>>(),
key in any::<[u8; 20]>()
) {
let mut rc = rc_Hmac::<sha1::Sha1>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[cfg(feature = "allow-non-fips")]
#[test]
fn rust_crypto_equivalence_many_updates_sha1(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 20]>()
) {
let mut rc = rc_Hmac::<sha1::Sha1>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Sha>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[cfg(feature = "allow-non-fips")]
#[test]
fn rust_crypto_equivalence_md5(
input in any::<BoundList<1024>>(),
key in any::<[u8; 16]>()
) {
let mut rc = rc_Hmac::<md5::Md5>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Md5>::new(&key);
rc.update(input.as_slice());
wc.update(input.as_slice()).unwrap();
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
#[cfg(feature = "allow-non-fips")]
#[test]
fn rust_crypto_equivalence_many_updates_md5(
input in any::<AnyList<32, BoundList<256>>>(),
key in any::<[u8; 16]>()
) {
let mut rc = rc_Hmac::<md5::Md5>::new_from_slice(key.as_slice())
.unwrap();
let mut wc = Hmac::<Md5>::new(&key);
for data in input.as_slice() {
rc.update(data.as_slice());
wc.update(data.as_slice()).unwrap();
}
let rc_out = rc.finalize().into_bytes();
prop_assert!(wc.compare_digest_slice(rc_out.as_slice()));
}
}
}