use crate::errors::UnknownCryptoError;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
pub(crate) trait HmacHashFunction: Clone {
const _BLOCKSIZE: usize;
const _OUTSIZE: usize;
fn _new() -> Self;
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>;
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>;
fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError>;
#[cfg(test)]
fn compare_state_to_other(&self, other: &Self);
}
pub(crate) trait HmacFunction {
const HASH_FUNC_OUTSIZE: usize;
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError>
where
Self: Sized;
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError>;
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError>;
fn _reset(&mut self);
}
const IPAD: u8 = 0x36;
const OPAD: u8 = 0x5C;
#[derive(Clone)]
pub(crate) struct Hmac<S: HmacHashFunction, const BLOCKSIZE: usize> {
working_hasher: S,
opad_hasher: S,
ipad_hasher: S,
is_finalized: bool,
}
impl<S: HmacHashFunction, const BLOCKSIZE: usize> core::fmt::Debug for Hmac<S, BLOCKSIZE> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"Hmac {{ working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: {:?} }}",
self.is_finalized
)
}
}
impl<S: HmacHashFunction, const BLOCKSIZE: usize> Hmac<S, BLOCKSIZE> {
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
debug_assert_eq!(S::_BLOCKSIZE, BLOCKSIZE);
let mut ipad = [IPAD; BLOCKSIZE];
if secret_key.len() > BLOCKSIZE {
debug_assert!(BLOCKSIZE > S::_OUTSIZE);
S::_digest(secret_key, &mut ipad[..S::_OUTSIZE])?;
for elem in ipad.iter_mut().take(S::_OUTSIZE) {
*elem ^= IPAD;
}
} else {
xor_slices!(secret_key, &mut ipad);
}
let mut ih = S::_new();
ih._update(&ipad)?;
for elem in ipad.iter_mut() {
*elem ^= IPAD ^ OPAD;
}
let mut oh = S::_new();
oh._update(&ipad)?;
#[cfg(feature = "zeroize")]
ipad.iter_mut().zeroize();
Ok(Self {
working_hasher: ih.clone(),
opad_hasher: oh,
ipad_hasher: ih,
is_finalized: false,
})
}
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
if self.is_finalized {
Err(UnknownCryptoError)
} else {
self.working_hasher._update(data)
}
}
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
debug_assert!(!dest.is_empty());
if self.is_finalized {
return Err(UnknownCryptoError);
}
self.is_finalized = true;
let mut outer_hasher = self.opad_hasher.clone();
self.working_hasher._finalize(dest)?;
outer_hasher._update(dest)?;
outer_hasher._finalize(dest)
}
fn _reset(&mut self) {
self.working_hasher = self.ipad_hasher.clone();
self.is_finalized = false;
}
#[cfg(test)]
pub(crate) fn compare_state_to_other(&self, other: &Self) {
self.working_hasher
.compare_state_to_other(&other.working_hasher);
self.opad_hasher.compare_state_to_other(&other.opad_hasher);
self.ipad_hasher.compare_state_to_other(&other.ipad_hasher);
assert_eq!(self.is_finalized, other.is_finalized);
}
}
pub mod sha256 {
use super::*;
use crate::hazardous::hash::sha2::sha256::{self, Sha256};
construct_hmac_key! {
(SecretKey, Sha256, sha256::SHA256_OUTSIZE, test_hmac_key, sha256::SHA256_BLOCKSIZE)
}
construct_tag! {
(Tag, test_tag, sha256::SHA256_OUTSIZE, sha256::SHA256_OUTSIZE)
}
impl_from_trait!(Tag, sha256::SHA256_OUTSIZE);
use super::Hmac;
#[derive(Clone, Debug)]
pub struct HmacSha256 {
_state: Hmac<Sha256, { sha256::SHA256_BLOCKSIZE }>,
}
impl HmacSha256 {
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Ok(Self {
_state: Hmac::<Sha256, { sha256::SHA256_BLOCKSIZE }>::_new(secret_key)?,
})
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn new(secret_key: &SecretKey) -> Self {
Self::_new(secret_key.unprotected_as_bytes()).unwrap()
}
pub fn reset(&mut self) {
self._state._reset()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
pub(crate) fn _finalize_internal(
&mut self,
dest: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
let mut dest = [0u8; sha256::SHA256_OUTSIZE];
self._finalize_internal(&mut dest)?;
Ok(Tag::from(dest))
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> {
let mut ctx = Self::new(secret_key);
ctx.update(data)?;
ctx.finalize()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &Tag,
secret_key: &SecretKey,
data: &[u8],
) -> Result<(), UnknownCryptoError> {
if &Self::hmac(secret_key, data)? == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
}
impl HmacFunction for HmacSha256 {
const HASH_FUNC_OUTSIZE: usize = sha256::SHA256_OUTSIZE;
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Self::_new(secret_key)
}
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
fn _reset(&mut self) {
self._state._reset()
}
}
#[cfg(test)]
mod public {
use super::*;
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate();
let initial_state = HmacSha256::new(&secret_key);
let debug = format!("{initial_state:?}");
let expected = "HmacSha256 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }";
assert_eq!(debug, expected);
}
#[cfg(feature = "safe_api")]
mod test_verify {
use super::*;
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_verify_diff_key_false(data: Vec<u8>) -> bool {
let sk = SecretKey::generate();
let mut state = HmacSha256::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate();
HmacSha256::verify(&tag, &bad_sk, &data[..]).is_err()
}
}
mod test_streaming_interface {
use super::*;
use crate::test_framework::incremental_interface::*;
const KEY: [u8; 32] = [0u8; 32];
impl TestableStreamingContext<Tag> for HmacSha256 {
fn reset(&mut self) -> Result<(), UnknownCryptoError> {
self.reset();
Ok(())
}
fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
self.update(input)
}
fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
self.finalize()
}
fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> {
HmacSha256::hmac(&SecretKey::from_slice(&KEY).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha256::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input)
}
fn compare_states(state_1: &HmacSha256, state_2: &HmacSha256) {
state_1._state.compare_state_to_other(&state_2._state);
}
}
#[test]
fn default_consistency_tests() {
let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha256>::new(
initial_state,
sha256::SHA256_BLOCKSIZE,
);
test_runner.run_all_tests();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_input_to_consistency(data: Vec<u8>) -> bool {
let initial_state = HmacSha256::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha256>::new(
initial_state,
sha256::SHA256_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
}
}
pub mod sha384 {
use super::*;
use crate::hazardous::hash::sha2::sha384::{self, Sha384};
construct_hmac_key! {
(SecretKey, Sha384, sha384::SHA384_OUTSIZE, test_hmac_key, sha384::SHA384_BLOCKSIZE)
}
construct_tag! {
(Tag, test_tag, sha384::SHA384_OUTSIZE, sha384::SHA384_OUTSIZE)
}
impl_from_trait!(Tag, sha384::SHA384_OUTSIZE);
use super::Hmac;
#[derive(Clone, Debug)]
pub struct HmacSha384 {
_state: Hmac<Sha384, { sha384::SHA384_BLOCKSIZE }>,
}
impl HmacSha384 {
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Ok(Self {
_state: Hmac::<Sha384, { sha384::SHA384_BLOCKSIZE }>::_new(secret_key)?,
})
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn new(secret_key: &SecretKey) -> Self {
Self::_new(secret_key.unprotected_as_bytes()).unwrap()
}
pub fn reset(&mut self) {
self._state._reset()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
pub(crate) fn _finalize_internal(
&mut self,
dest: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
let mut dest = [0u8; sha384::SHA384_OUTSIZE];
self._finalize_internal(&mut dest)?;
Ok(Tag::from(dest))
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> {
let mut ctx = Self::new(secret_key);
ctx.update(data)?;
ctx.finalize()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &Tag,
secret_key: &SecretKey,
data: &[u8],
) -> Result<(), UnknownCryptoError> {
if &Self::hmac(secret_key, data)? == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
}
impl HmacFunction for HmacSha384 {
const HASH_FUNC_OUTSIZE: usize = sha384::SHA384_OUTSIZE;
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Self::_new(secret_key)
}
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
fn _reset(&mut self) {
self._state._reset()
}
}
#[cfg(test)]
mod public {
use super::*;
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate();
let initial_state = HmacSha384::new(&secret_key);
let debug = format!("{initial_state:?}");
let expected = "HmacSha384 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }";
assert_eq!(debug, expected);
}
#[cfg(feature = "safe_api")]
mod test_verify {
use super::*;
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_verify_diff_key_false(data: Vec<u8>) -> bool {
let sk = SecretKey::generate();
let mut state = HmacSha384::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate();
HmacSha384::verify(&tag, &bad_sk, &data[..]).is_err()
}
}
mod test_streaming_interface {
use super::*;
use crate::test_framework::incremental_interface::*;
const KEY: [u8; 32] = [0u8; 32];
impl TestableStreamingContext<Tag> for HmacSha384 {
fn reset(&mut self) -> Result<(), UnknownCryptoError> {
self.reset();
Ok(())
}
fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
self.update(input)
}
fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
self.finalize()
}
fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> {
HmacSha384::hmac(&SecretKey::from_slice(&KEY).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha384::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input)
}
fn compare_states(state_1: &HmacSha384, state_2: &HmacSha384) {
state_1._state.compare_state_to_other(&state_2._state);
}
}
#[test]
fn default_consistency_tests() {
let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha384>::new(
initial_state,
sha384::SHA384_BLOCKSIZE,
);
test_runner.run_all_tests();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_input_to_consistency(data: Vec<u8>) -> bool {
let initial_state = HmacSha384::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha384>::new(
initial_state,
sha384::SHA384_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
}
}
pub mod sha512 {
use super::*;
use crate::hazardous::hash::sha2::sha512::{self, Sha512};
construct_hmac_key! {
(SecretKey, Sha512, sha512::SHA512_OUTSIZE, test_hmac_key, sha512::SHA512_BLOCKSIZE)
}
construct_tag! {
(Tag, test_tag, sha512::SHA512_OUTSIZE, sha512::SHA512_OUTSIZE)
}
impl_from_trait!(Tag, sha512::SHA512_OUTSIZE);
use super::Hmac;
#[derive(Clone, Debug)]
pub struct HmacSha512 {
_state: Hmac<Sha512, { sha512::SHA512_BLOCKSIZE }>,
}
impl HmacSha512 {
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Ok(Self {
_state: Hmac::<Sha512, { sha512::SHA512_BLOCKSIZE }>::_new(secret_key)?,
})
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn new(secret_key: &SecretKey) -> Self {
Self::_new(secret_key.unprotected_as_bytes()).unwrap()
}
pub fn reset(&mut self) {
self._state._reset()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
pub(crate) fn _finalize_internal(
&mut self,
dest: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
let mut dest = [0u8; sha512::SHA512_OUTSIZE];
self._finalize_internal(&mut dest)?;
Ok(Tag::from(dest))
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn hmac(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> {
let mut ctx = Self::new(secret_key);
ctx.update(data)?;
ctx.finalize()
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &Tag,
secret_key: &SecretKey,
data: &[u8],
) -> Result<(), UnknownCryptoError> {
if &Self::hmac(secret_key, data)? == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
}
impl HmacFunction for HmacSha512 {
const HASH_FUNC_OUTSIZE: usize = sha512::SHA512_OUTSIZE;
fn _new(secret_key: &[u8]) -> Result<Self, UnknownCryptoError> {
Self::_new(secret_key)
}
fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
self._state._update(data)
}
fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
self._state._finalize(dest)
}
fn _reset(&mut self) {
self._state._reset()
}
}
#[cfg(test)]
mod public {
use super::*;
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate();
let initial_state = HmacSha512::new(&secret_key);
let debug = format!("{initial_state:?}");
let expected = "HmacSha512 { _state: Hmac { working_hasher: [***OMITTED***], opad_hasher: [***OMITTED***], ipad_hasher: [***OMITTED***], is_finalized: false } }";
assert_eq!(debug, expected);
}
#[cfg(feature = "safe_api")]
mod test_verify {
use super::*;
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_verify_diff_key_false(data: Vec<u8>) -> bool {
let sk = SecretKey::generate();
let mut state = HmacSha512::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate();
HmacSha512::verify(&tag, &bad_sk, &data[..]).is_err()
}
}
mod test_streaming_interface {
use super::*;
use crate::test_framework::incremental_interface::*;
const KEY: [u8; 32] = [0u8; 32];
impl TestableStreamingContext<Tag> for HmacSha512 {
fn reset(&mut self) -> Result<(), UnknownCryptoError> {
self.reset();
Ok(())
}
fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
self.update(input)
}
fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
self.finalize()
}
fn one_shot(input: &[u8]) -> Result<Tag, UnknownCryptoError> {
HmacSha512::hmac(&SecretKey::from_slice(&KEY).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha512::verify(expected, &SecretKey::from_slice(&KEY).unwrap(), input)
}
fn compare_states(state_1: &HmacSha512, state_2: &HmacSha512) {
state_1._state.compare_state_to_other(&state_2._state);
}
}
#[test]
fn default_consistency_tests() {
let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha512>::new(
initial_state,
sha512::SHA512_BLOCKSIZE,
);
test_runner.run_all_tests();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_input_to_consistency(data: Vec<u8>) -> bool {
let initial_state = HmacSha512::new(&SecretKey::from_slice(&KEY).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha512>::new(
initial_state,
sha512::SHA512_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
}
}