use crate::errors::UnknownCryptoError;
use crate::generics::GenerateSecret;
#[cfg(feature = "safe_api")]
use crate::generics::sealed::Data;
use crate::generics::sealed::Sealed;
use crate::generics::{ByteArrayVecData, Secret, TypeSpec};
pub use crate::hazardous::hash::blake2::blake2b::{
BLAKE2B_MAX_KEYSIZE, BLAKE2B_MAX_OUTSIZE, BLAKE2B_MIN_KEYSIZE, BLAKE2B_MIN_OUTSIZE,
};
use crate::hazardous::hash::blake2::blake2b_core;
#[cfg(feature = "serde")]
use alloc::vec::Vec;
pub const BLAKE2B_GEN_SIZE: usize = 32;
#[derive(Debug)]
pub struct Blake2bKey {}
impl Sealed for Blake2bKey {}
impl TypeSpec for Blake2bKey {
const NAME: &'static str = stringify!(SecretKey);
type TypeData = ByteArrayVecData<BLAKE2B_MIN_KEYSIZE, BLAKE2B_MAX_KEYSIZE>;
}
impl GenerateSecret for Blake2bKey {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
fn generate() -> Result<Secret<Blake2bKey>, UnknownCryptoError> {
let mut data = Self::TypeData::new(32)?;
crate::util::secure_rand_bytes(&mut data.bytes)?;
Ok(Secret::from_data(data))
}
}
pub type SecretKey = Secret<Blake2bKey>;
#[derive(Debug, Clone)]
pub struct Blake2bTag {}
impl Sealed for Blake2bTag {}
impl TypeSpec for Blake2bTag {
const NAME: &'static str = stringify!(Tag);
type TypeData = ByteArrayVecData<BLAKE2B_MIN_OUTSIZE, BLAKE2B_MAX_OUTSIZE>;
}
pub type Tag = Secret<Blake2bTag>;
#[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)
}
}
#[derive(Debug, Clone)]
pub struct Blake2b {
_state: blake2b_core::State,
}
impl Blake2b {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn new(secret_key: &SecretKey, size: usize) -> Result<Self, UnknownCryptoError> {
Ok(Self {
_state: blake2b_core::State::_new(secret_key.unprotected_as_ref(), size)?,
})
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn reset(&mut self, secret_key: &SecretKey) -> Result<(), UnknownCryptoError> {
self._state._reset(secret_key.unprotected_as_ref())
}
#[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)
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn finalize(&mut self) -> Result<Tag, UnknownCryptoError> {
let mut tmp = zeroize_wrap!([0u8; BLAKE2B_MAX_OUTSIZE]);
self._state._finalize(&mut tmp)?;
Tag::try_from(&tmp[..self._state.size])
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn verify(
expected: &Tag,
secret_key: &SecretKey,
size: usize,
data: &[u8],
) -> Result<(), UnknownCryptoError> {
let mut ctx = Self::new(secret_key, size)?;
ctx.update(data)?;
if &ctx.finalize()? == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
}
#[cfg(test)]
mod public {
use super::*;
#[test]
fn test_blake2b_key() {
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_with_generate::<BLAKE2B_MIN_KEYSIZE, BLAKE2B_MAX_KEYSIZE, 32, Blake2bKey>(
);
}
#[test]
fn test_blake2b_tag() {
use crate::test_framework::newtypes::secret::SecretNewtype;
SecretNewtype::test_no_generate::<BLAKE2B_MIN_OUTSIZE, BLAKE2B_MAX_OUTSIZE, Blake2bTag>();
}
#[test]
#[cfg(feature = "serde")]
fn test_serde_serialized_equivalence_to_bytes_fn() {
let bytes = [38u8; BLAKE2B_MAX_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; BLAKE2B_MAX_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());
}
mod test_streaming_interface_no_key {
use super::*;
use crate::errors::UnknownCryptoError;
use crate::hazardous::hash::blake2::blake2b_core::{
BLAKE2B_BLOCKSIZE, compare_blake2b_states,
};
use crate::hazardous::mac::blake2b::{Blake2b, SecretKey, Tag};
use crate::test_framework::incremental_interface::{
StreamingContextConsistencyTester, TestableStreamingContext,
};
const KEY: [u8; 32] = [255u8; 32];
impl TestableStreamingContext<Tag> for Blake2b {
fn reset(&mut self) -> Result<(), UnknownCryptoError> {
let key = SecretKey::try_from(KEY.as_slice()).unwrap();
self.reset(&key)
}
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> {
let key = SecretKey::try_from(KEY.as_slice()).unwrap();
let mut ctx = Blake2b::new(&key, BLAKE2B_MAX_OUTSIZE)?;
ctx.update(input)?;
ctx.finalize()
}
fn verify_result(expected: &Tag, input: &[u8]) -> Result<(), UnknownCryptoError> {
let actual = Self::one_shot(input)?;
if &actual == expected {
Ok(())
} else {
Err(UnknownCryptoError)
}
}
fn compare_states(state_1: &Blake2b, state_2: &Blake2b) {
compare_blake2b_states(&state_1._state, &state_2._state)
}
}
#[test]
fn default_consistency_tests() {
let key = SecretKey::try_from(KEY.as_slice()).unwrap();
let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_MAX_OUTSIZE).unwrap();
let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new(
initial_state,
BLAKE2B_BLOCKSIZE,
);
test_runner.run_all_tests();
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_input_to_consistency(data: Vec<u8>) -> bool {
let key = SecretKey::try_from(KEY.as_slice()).unwrap();
let initial_state: Blake2b = Blake2b::new(&key, BLAKE2B_MAX_OUTSIZE).unwrap();
let test_runner = StreamingContextConsistencyTester::<Tag, Blake2b>::new(
initial_state,
BLAKE2B_BLOCKSIZE,
);
test_runner.run_all_tests_property(&data);
true
}
}
mod test_new {
use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
#[test]
fn test_init_size() {
let sk = SecretKey::try_from([0u8; 32].as_slice()).unwrap();
assert!(Blake2b::new(&sk, 0).is_err());
assert!(Blake2b::new(&sk, 65).is_err());
assert!(Blake2b::new(&sk, 1).is_ok());
assert!(Blake2b::new(&sk, 64).is_ok());
}
}
#[cfg(feature = "safe_api")]
mod test_verify {
use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_verify_diff_key_false(data: Vec<u8>) -> bool {
let sk = SecretKey::generate().unwrap();
let mut state = Blake2b::new(&sk, 64).unwrap();
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
let bad_sk = SecretKey::generate().unwrap();
Blake2b::verify(&tag, &bad_sk, 64, &data[..]).is_err()
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_verify_diff_size_false(data: Vec<u8>, size_one: usize, size_two: usize) -> bool {
let (size_one, size_two) = match (size_one, size_two) {
(1..=64, 1..=64) => (size_one, size_two),
(_, _) => (32, 64),
};
let sk = SecretKey::generate().unwrap();
let mut state = Blake2b::new(&sk, size_one).unwrap();
state.update(&data[..]).unwrap();
let tag = state.finalize().unwrap();
if size_one != size_two {
Blake2b::verify(&tag, &sk, size_two, &data[..]).is_err()
} else {
Blake2b::verify(&tag, &sk, size_two, &data[..]).is_ok()
}
}
}
mod test_streaming_interface {
use crate::hazardous::hash::blake2::blake2b_core::compare_blake2b_states;
use crate::hazardous::mac::blake2b::{Blake2b, SecretKey};
fn produces_same_hash(sk: &SecretKey, size: usize, data: &[u8]) {
let mut state_1 = Blake2b::new(sk, size).unwrap();
state_1.update(data).unwrap();
let res_1 = state_1.finalize().unwrap();
let mut state_2 = Blake2b::new(sk, size).unwrap();
state_2.reset(sk).unwrap();
state_2.update(data).unwrap();
let res_2 = state_2.finalize().unwrap();
let mut state_3 = Blake2b::new(sk, size).unwrap();
state_3.update(data).unwrap();
state_3.reset(sk).unwrap();
state_3.update(data).unwrap();
let res_3 = state_3.finalize().unwrap();
let mut state_4 = Blake2b::new(sk, size).unwrap();
state_4.update(data).unwrap();
let _ = state_4.finalize().unwrap();
state_4.reset(sk).unwrap();
state_4.update(data).unwrap();
let res_4 = state_4.finalize().unwrap();
assert_eq!(res_1, res_2);
assert_eq!(res_2, res_3);
assert_eq!(res_3, res_4);
if data.is_empty() {
let mut state_5 = Blake2b::new(sk, size).unwrap();
let res_5 = state_5.finalize().unwrap();
let mut state_6 = Blake2b::new(sk, size).unwrap();
state_6.reset(sk).unwrap();
let res_6 = state_6.finalize().unwrap();
let mut state_7 = Blake2b::new(sk, size).unwrap();
state_7.update(b"Wrong data").unwrap();
state_7.reset(sk).unwrap();
let res_7 = state_7.finalize().unwrap();
assert_eq!(res_4, res_5);
assert_eq!(res_5, res_6);
assert_eq!(res_6, res_7);
}
}
fn produces_same_state(sk: &SecretKey, size: usize, data: &[u8]) {
let state_1 = Blake2b::new(sk, size).unwrap();
let mut state_2 = Blake2b::new(sk, size).unwrap();
state_2.reset(sk).unwrap();
let mut state_3 = Blake2b::new(sk, size).unwrap();
state_3.update(data).unwrap();
state_3.reset(sk).unwrap();
let mut state_4 = Blake2b::new(sk, size).unwrap();
state_4.update(data).unwrap();
let _ = state_4.finalize().unwrap();
state_4.reset(sk).unwrap();
compare_blake2b_states(&state_1._state, &state_2._state);
compare_blake2b_states(&state_2._state, &state_3._state);
compare_blake2b_states(&state_3._state, &state_4._state);
}
#[test]
fn test_produce_same_state() {
let sk = SecretKey::try_from(b"Testing".as_slice()).unwrap();
produces_same_state(&sk, 1, b"Tests");
produces_same_state(&sk, 32, b"Tests");
produces_same_state(&sk, 64, b"Tests");
produces_same_state(&sk, 28, b"Tests");
}
#[test]
fn test_produce_same_hash() {
let sk = SecretKey::try_from(b"Testing".as_slice()).unwrap();
produces_same_hash(&sk, 1, b"Tests");
produces_same_hash(&sk, 32, b"Tests");
produces_same_hash(&sk, 64, b"Tests");
produces_same_hash(&sk, 28, b"Tests");
produces_same_hash(&sk, 1, b"");
produces_same_hash(&sk, 32, b"");
produces_same_hash(&sk, 64, b"");
produces_same_hash(&sk, 28, b"");
}
#[test]
fn test_reset_uses_correct_key() {
let message = b"message";
let taglen = 32usize;
let k1_32 = SecretKey::try_from(&[0x11; 32]).unwrap();
let k2_32 = SecretKey::try_from(&[0x33; 32]).unwrap();
let k3_33 = SecretKey::try_from(&[0x22; 64]).unwrap();
let mut mac = Blake2b::new(&k1_32, taglen).unwrap();
mac.update(message).unwrap();
let t1 = mac.finalize().unwrap();
assert!(Blake2b::verify(&t1, &k1_32, taglen, message).is_ok());
mac.reset(&k2_32).unwrap();
mac.update(message).unwrap();
let t2 = mac.finalize().unwrap();
assert!(Blake2b::verify(&t2, &k2_32, taglen, message).is_ok());
assert_eq!(
t2.unprotected_as_ref(),
&hex::decode("c053d9657b3d5e0b8ecc62235f25936008981da8f8ae9456e5fe2517a1b65153")
.unwrap()
);
mac.reset(&k3_33).unwrap();
mac.update(message).unwrap();
let t3 = mac.finalize().unwrap();
assert!(Blake2b::verify(&t3, &k3_33, taglen, message).is_ok());
assert_eq!(
t3.unprotected_as_ref(),
&hex::decode("fa0e4d6c7974a2021f96f30e98e8564430441e460f32e98de2cea59c320271d5")
.unwrap()
);
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_same_hash_different_usage(data: Vec<u8>, size: usize) -> bool {
use crate::hazardous::mac::blake2b::{BLAKE2B_MAX_OUTSIZE, BLAKE2B_MIN_OUTSIZE};
if (BLAKE2B_MIN_OUTSIZE..=BLAKE2B_MAX_OUTSIZE).contains(&size) {
let sk = SecretKey::generate().unwrap();
produces_same_hash(&sk, size, &data[..]);
}
true
}
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_same_state_different_usage(data: Vec<u8>, size: usize) -> bool {
use crate::hazardous::mac::blake2b::{BLAKE2B_MAX_OUTSIZE, BLAKE2B_MIN_OUTSIZE};
if (BLAKE2B_MIN_OUTSIZE..=BLAKE2B_MAX_OUTSIZE).contains(&size) {
let sk = SecretKey::generate().unwrap();
produces_same_state(&sk, size, &data[..]);
}
true
}
}
}