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::generics::GenerateSecret;
use crate::{
generics::{ByteArrayData, Secret, TypeSpec, sealed::Data, sealed::Sealed},
hazardous::hash::sha2::sha256::{self, Sha256},
};
#[cfg(feature = "serde")]
use alloc::vec::Vec;
#[derive(Debug)]
pub struct HmacSha256Key {}
impl Sealed for HmacSha256Key {}
impl TypeSpec for HmacSha256Key {
const NAME: &'static str = stringify!(SecretKey);
type TypeData = ByteArrayData<{ sha256::SHA256_BLOCKSIZE }>;
fn parse_bytes(bytes: &[u8]) -> Result<Self::TypeData, UnknownCryptoError> {
let mut data = ByteArrayData::new(sha256::SHA256_BLOCKSIZE)?;
if bytes.len() > sha256::SHA256_BLOCKSIZE {
Sha256::_digest(bytes, &mut data.bytes[..sha256::SHA256_OUTSIZE])?;
} else {
data.bytes[..bytes.len()].copy_from_slice(bytes);
}
Ok(data)
}
}
impl GenerateSecret for HmacSha256Key {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
fn generate() -> Result<Secret<HmacSha256Key>, UnknownCryptoError> {
let mut data = Self::TypeData::new(sha256::SHA256_BLOCKSIZE)?;
crate::util::secure_rand_bytes(&mut data.bytes)?;
Ok(Secret::from_data(data))
}
}
pub type SecretKey = Secret<HmacSha256Key>;
#[derive(Debug, Clone)]
pub struct HmacSha256Tag {}
impl Sealed for HmacSha256Tag {}
impl TypeSpec for HmacSha256Tag {
const NAME: &'static str = stringify!(Tag);
type TypeData = ByteArrayData<{ sha256::SHA256_OUTSIZE }>;
}
impl From<[u8; sha256::SHA256_OUTSIZE]> for Secret<HmacSha256Tag> {
fn from(value: [u8; sha256::SHA256_OUTSIZE]) -> Self {
Self::from_data(<HmacSha256Tag as TypeSpec>::TypeData::from(value))
}
}
pub type Tag = Secret<HmacSha256Tag>;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::Serialize for Tag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let bytes: &[u8] = self.data.as_ref();
bytes.serialize(serializer)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::Deserialize<'de> for Tag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
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_ref()).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]
fn test_key() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_with_generate::<
{ sha256::SHA256_BLOCKSIZE },
{ sha256::SHA256_BLOCKSIZE },
{ sha256::SHA256_BLOCKSIZE },
HmacSha256Key,
>();
}
#[test]
fn test_tag() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_no_generate::<
{ sha256::SHA256_OUTSIZE },
{ sha256::SHA256_OUTSIZE },
HmacSha256Tag,
>();
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha256::SHA256_OUTSIZE];
let secret_type = Tag::try_from(&bytes).unwrap();
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let serialized_from_secret_type = serde_json::to_value(&secret_type).unwrap();
assert_eq!(serialized_from_bytes, serialized_from_secret_type);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_deserialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha256::SHA256_OUTSIZE];
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let secret_type: Tag = serde_json::from_value(serialized_from_bytes).unwrap();
assert_eq!(secret_type.unprotected_as_ref(), bytes.as_slice());
}
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate().unwrap();
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().unwrap();
let mut state = HmacSha256::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate().unwrap();
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::try_from(KEY.as_slice()).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha256::verify(
expected,
&SecretKey::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).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::generics::GenerateSecret;
use crate::{
generics::{ByteArrayData, Secret, TypeSpec, sealed::Data, sealed::Sealed},
hazardous::hash::sha2::sha384::{self, Sha384},
};
#[cfg(feature = "serde")]
use alloc::vec::Vec;
#[derive(Debug)]
pub struct HmacSha384Key {}
impl Sealed for HmacSha384Key {}
impl TypeSpec for HmacSha384Key {
const NAME: &'static str = stringify!(SecretKey);
type TypeData = ByteArrayData<{ sha384::SHA384_BLOCKSIZE }>;
fn parse_bytes(bytes: &[u8]) -> Result<Self::TypeData, UnknownCryptoError> {
let mut data = ByteArrayData::new(sha384::SHA384_BLOCKSIZE)?;
if bytes.len() > sha384::SHA384_BLOCKSIZE {
Sha384::_digest(bytes, &mut data.bytes[..sha384::SHA384_OUTSIZE])?;
} else {
data.bytes[..bytes.len()].copy_from_slice(bytes);
}
Ok(data)
}
}
impl GenerateSecret for HmacSha384Key {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
fn generate() -> Result<Secret<HmacSha384Key>, UnknownCryptoError> {
let mut data = Self::TypeData::new(sha384::SHA384_BLOCKSIZE)?;
crate::util::secure_rand_bytes(&mut data.bytes)?;
Ok(Secret::from_data(data))
}
}
pub type SecretKey = Secret<HmacSha384Key>;
#[derive(Debug, Clone)]
pub struct HmacSha384Tag {}
impl Sealed for HmacSha384Tag {}
impl TypeSpec for HmacSha384Tag {
const NAME: &'static str = stringify!(Tag);
type TypeData = ByteArrayData<{ sha384::SHA384_OUTSIZE }>;
}
impl From<[u8; sha384::SHA384_OUTSIZE]> for Secret<HmacSha384Tag> {
fn from(value: [u8; sha384::SHA384_OUTSIZE]) -> Self {
Self::from_data(<HmacSha384Tag as TypeSpec>::TypeData::from(value))
}
}
pub type Tag = Secret<HmacSha384Tag>;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::Serialize for Tag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let bytes: &[u8] = self.data.as_ref();
bytes.serialize(serializer)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::Deserialize<'de> for Tag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
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_ref()).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]
fn test_key() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_with_generate::<
{ sha384::SHA384_BLOCKSIZE },
{ sha384::SHA384_BLOCKSIZE },
{ sha384::SHA384_BLOCKSIZE },
HmacSha384Key,
>();
}
#[test]
fn test_tag() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_no_generate::<
{ sha384::SHA384_OUTSIZE },
{ sha384::SHA384_OUTSIZE },
HmacSha384Tag,
>();
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha384::SHA384_OUTSIZE];
let secret_type = Tag::try_from(&bytes).unwrap();
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let serialized_from_secret_type = serde_json::to_value(&secret_type).unwrap();
assert_eq!(serialized_from_bytes, serialized_from_secret_type);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_deserialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha384::SHA384_OUTSIZE];
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let secret_type: Tag = serde_json::from_value(serialized_from_bytes).unwrap();
assert_eq!(secret_type.unprotected_as_ref(), bytes.as_slice());
}
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate().unwrap();
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().unwrap();
let mut state = HmacSha384::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate().unwrap();
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::try_from(KEY.as_slice()).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha384::verify(
expected,
&SecretKey::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).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::generics::GenerateSecret;
use crate::{
generics::{ByteArrayData, Secret, TypeSpec, sealed::Data, sealed::Sealed},
hazardous::hash::sha2::sha512::{self, Sha512},
};
#[cfg(feature = "serde")]
use alloc::vec::Vec;
#[derive(Debug)]
pub struct HmacSha512Key {}
impl Sealed for HmacSha512Key {}
impl TypeSpec for HmacSha512Key {
const NAME: &'static str = stringify!(SecretKey);
type TypeData = ByteArrayData<{ sha512::SHA512_BLOCKSIZE }>;
fn parse_bytes(bytes: &[u8]) -> Result<Self::TypeData, UnknownCryptoError> {
let mut data = ByteArrayData::new(sha512::SHA512_BLOCKSIZE)?;
if bytes.len() > sha512::SHA512_BLOCKSIZE {
Sha512::_digest(bytes, &mut data.bytes[..sha512::SHA512_OUTSIZE])?;
} else {
data.bytes[..bytes.len()].copy_from_slice(bytes);
}
Ok(data)
}
}
impl GenerateSecret for HmacSha512Key {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
fn generate() -> Result<Secret<HmacSha512Key>, UnknownCryptoError> {
let mut data = Self::TypeData::new(sha512::SHA512_BLOCKSIZE)?;
crate::util::secure_rand_bytes(&mut data.bytes)?;
Ok(Secret::from_data(data))
}
}
pub type SecretKey = Secret<HmacSha512Key>;
#[derive(Debug, Clone)]
pub struct HmacSha512Tag {}
impl Sealed for HmacSha512Tag {}
impl TypeSpec for HmacSha512Tag {
const NAME: &'static str = stringify!(Tag);
type TypeData = ByteArrayData<{ sha512::SHA512_OUTSIZE }>;
}
impl From<[u8; sha512::SHA512_OUTSIZE]> for Secret<HmacSha512Tag> {
fn from(value: [u8; sha512::SHA512_OUTSIZE]) -> Self {
Self::from_data(<HmacSha512Tag as TypeSpec>::TypeData::from(value))
}
}
pub type Tag = Secret<HmacSha512Tag>;
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::Serialize for Tag {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let bytes: &[u8] = self.data.as_ref();
bytes.serialize(serializer)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::Deserialize<'de> for Tag {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
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_ref()).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]
fn test_key() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_with_generate::<
{ sha512::SHA512_BLOCKSIZE },
{ sha512::SHA512_BLOCKSIZE },
{ sha512::SHA512_BLOCKSIZE },
HmacSha512Key,
>();
}
#[test]
fn test_tag() {
use super::*;
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_no_generate::<
{ sha512::SHA512_OUTSIZE },
{ sha512::SHA512_OUTSIZE },
HmacSha512Tag,
>();
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha512::SHA512_OUTSIZE];
let secret_type = Tag::try_from(&bytes).unwrap();
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let serialized_from_secret_type = serde_json::to_value(&secret_type).unwrap();
assert_eq!(serialized_from_bytes, serialized_from_secret_type);
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_deserialized_equivalence_to_bytes_fn() {
let bytes = [38u8; sha512::SHA512_OUTSIZE];
let serialized_from_bytes = serde_json::to_value(bytes.as_slice()).unwrap();
let secret_type: Tag = serde_json::from_value(serialized_from_bytes).unwrap();
assert_eq!(secret_type.unprotected_as_ref(), bytes.as_slice());
}
#[test]
#[cfg(feature = "safe_api")]
fn test_debug_impl() {
let secret_key = SecretKey::generate().unwrap();
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().unwrap();
let mut state = HmacSha512::new(&sk);
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate().unwrap();
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::try_from(KEY.as_slice()).unwrap(), input)
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
HmacSha512::verify(
expected,
&SecretKey::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).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::try_from(KEY.as_slice()).unwrap());
let test_runner = StreamingContextConsistencyTester::<Tag, HmacSha512>::new(
initial_state,
sha512::SHA512_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
}
}