#[cfg(feature = "safe_api")]
macro_rules! impl_default_trait (($name:ident, $size:expr) => (
impl Default for $name {
#[cfg(feature = "safe_api")]
fn default() -> $name {
let mut value = vec![0u8; $size];
crate::util::secure_rand_bytes(&mut value).unwrap();
$name { value: value, original_length: $size }
}
}
));
macro_rules! impl_ct_partialeq_trait (($name:ident, $bytes_function:ident) => (
impl PartialEq<$name> for $name {
fn eq(&self, other: &$name) -> bool {
use subtle::ConstantTimeEq;
(self.$bytes_function()
.ct_eq(other.$bytes_function())).into()
}
}
impl Eq for $name {}
impl PartialEq<&[u8]> for $name {
fn eq(&self, other: &&[u8]) -> bool {
use subtle::ConstantTimeEq;
(self.$bytes_function()
.ct_eq(*other)).into()
}
}
));
macro_rules! impl_omitted_debug_trait (($name:ident) => (
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} {{***OMITTED***}}", stringify!($name))
}
}
));
macro_rules! impl_normal_debug_trait (($name:ident) => (
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} {:?}", stringify!($name), &self.value[..])
}
}
));
macro_rules! impl_drop_trait (($name:ident) => (
impl Drop for $name {
fn drop(&mut self) {
use zeroize::Zeroize;
self.value.iter_mut().zeroize();
}
}
));
macro_rules! impl_asref_trait (($name:ident) => (
impl AsRef<[u8]> for $name {
#[inline]
fn as_ref(&self) -> &[u8] {
self.value[..self.original_length].as_ref()
}
}
));
macro_rules! impl_from_trait (($name:ident, $size:expr) => (
impl From<[u8; $size]> for $name {
#[inline]
fn from(bytes: [u8; $size]) -> $name {
$name {
value: bytes,
original_length: $size
}
}
}
));
macro_rules! func_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => (
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
#[allow(clippy::double_comparisons)]
pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> {
let slice_len = slice.len();
if slice_len < $lower_bound || slice_len > $upper_bound {
return Err(UnknownCryptoError);
}
let mut value = [0u8; $upper_bound];
value[..slice_len].copy_from_slice(slice);
Ok($name { value: value, original_length: slice_len })
}
));
#[cfg(feature = "safe_api")]
macro_rules! func_from_slice_variable_size (($name:ident) => (
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
#[cfg(feature = "safe_api")]
pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> {
if slice.is_empty() {
return Err(UnknownCryptoError);
}
Ok($name { value: Vec::from(slice), original_length: slice.len() })
}
));
macro_rules! func_unprotected_as_bytes (() => (
#[inline]
pub fn unprotected_as_bytes(&self) -> &[u8] {
self.value[..self.original_length].as_ref()
}
));
macro_rules! func_len (() => (
#[inline]
pub fn len(&self) -> usize {
self.original_length
}
));
macro_rules! func_generate (($name:ident, $upper_bound:expr, $gen_length:expr) => (
#[cfg(feature = "safe_api")]
pub fn generate() -> $name {
let mut value = [0u8; $upper_bound];
crate::util::secure_rand_bytes(&mut value[..$gen_length]).unwrap();
$name { value: value, original_length: $gen_length }
}
));
#[cfg(feature = "safe_api")]
macro_rules! func_generate_variable_size (($name:ident) => (
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
#[cfg(feature = "safe_api")]
pub fn generate(length: usize) -> Result<$name, UnknownCryptoError> {
if length < 1 || length >= (u32::max_value() as usize) {
return Err(UnknownCryptoError);
}
let mut value = vec![0u8; length];
crate::util::secure_rand_bytes(&mut value).unwrap();
Ok($name { value: value, original_length: length })
}
));
#[cfg(test)]
macro_rules! test_bound_parameters (($name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr) => (
#[test]
fn test_bound_params() {
assert!($lower_bound <= $upper_bound);
assert!($gen_length <= $upper_bound);
assert!($gen_length >= $lower_bound);
}
));
#[cfg(test)]
macro_rules! test_partial_eq (($name:ident, $upper_bound:expr) => (
#[test]
fn test_partial_eq() {
assert!($name::from_slice(&[0u8; $upper_bound]).unwrap() == $name::from_slice(&[0u8; $upper_bound]).unwrap());
assert!($name::from_slice(&[0u8; $upper_bound]).unwrap() != $name::from_slice(&[1u8; $upper_bound]).unwrap());
assert!($name::from_slice(&[0u8; $upper_bound]).unwrap() == [0u8; $upper_bound].as_ref());
assert!($name::from_slice(&[0u8; $upper_bound]).unwrap() != [1u8; $upper_bound].as_ref());
}
));
#[cfg(test)]
macro_rules! test_from_slice (($name:ident, $lower_bound:expr, $upper_bound:expr) => (
#[test]
fn test_from_slice() {
assert!($name::from_slice(&[0u8; $upper_bound]).is_ok());
assert!($name::from_slice(&[0u8; $lower_bound]).is_ok());
assert!($name::from_slice(&[0u8; $upper_bound + 1]).is_err());
assert!($name::from_slice(&[0u8; $lower_bound - 1]).is_err());
assert!($name::from_slice(&[0u8; 0]).is_err());
if $upper_bound != $lower_bound {
assert!($name::from_slice(&[0u8; $upper_bound - 1]).is_ok());
assert!($name::from_slice(&[0u8; $lower_bound + 1]).is_ok());
}
}
));
#[cfg(test)]
macro_rules! test_as_bytes_and_get_length (($name:ident, $lower_bound:expr, $upper_bound:expr, $bytes_function:ident) => (
#[test]
fn test_as_bytes() {
let test_upper = $name::from_slice(&[0u8; $upper_bound]).unwrap();
let test_lower = $name::from_slice(&[0u8; $lower_bound]).unwrap();
assert!(test_upper.$bytes_function().len() == test_upper.len());
assert!(test_upper.len() == $upper_bound);
assert!(test_lower.$bytes_function().len() == test_lower.len());
assert!(test_lower.len() == $lower_bound);
if $lower_bound != $upper_bound {
let test_upper = $name::from_slice(&[0u8; $upper_bound - 1]).unwrap();
let test_lower = $name::from_slice(&[0u8; $lower_bound + 1]).unwrap();
assert!(test_upper.$bytes_function().len() == test_upper.len());
assert!(test_upper.len() == $upper_bound - 1);
assert!(test_lower.$bytes_function().len() == test_lower.len());
assert!(test_lower.len() == $lower_bound + 1);
}
}
));
#[cfg(test)]
#[cfg(feature = "safe_api")]
macro_rules! test_generate (($name:ident, $gen_length:expr) => (
#[test]
#[cfg(feature = "safe_api")]
fn test_generate() {
let test_zero = $name::from_slice(&[0u8; $gen_length]).unwrap();
let test_rand = $name::generate();
assert!(test_zero != test_rand);
assert!(test_rand.len() == $gen_length);
}
));
#[cfg(test)]
#[cfg(feature = "safe_api")]
macro_rules! test_omitted_debug (($name:ident, $upper_bound:expr) => (
#[test]
#[cfg(feature = "safe_api")]
fn test_omitted_debug() {
let secret = format!("{:?}", [0u8; $upper_bound].as_ref());
let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap());
assert_eq!(test_debug_contents.contains(&secret), false);
}
));
#[cfg(test)]
#[cfg(feature = "safe_api")]
macro_rules! test_normal_debug (($name:ident, $upper_bound:expr) => (
#[test]
#[cfg(feature = "safe_api")]
fn test_normal_debug() {
let public = format!("{:?}", [0u8; $upper_bound].as_ref());
let test_debug_contents = format!("{:?}", $name::from_slice(&[0u8; $upper_bound]).unwrap());
assert_eq!(test_debug_contents.contains(&public), true);
}
));
#[cfg(test)]
#[cfg(feature = "safe_api")]
macro_rules! test_from_slice_variable (($name:ident) => (
#[test]
#[cfg(feature = "safe_api")]
fn test_from_slice_variable() {
assert!($name::from_slice(&[0u8; 512]).is_ok());
assert!($name::from_slice(&[0u8; 256]).is_ok());
assert!($name::from_slice(&[0u8; 1]).is_ok());
assert!($name::from_slice(&[0u8; 0]).is_err());
}
));
#[cfg(test)]
#[cfg(feature = "safe_api")]
macro_rules! test_generate_variable (($name:ident) => (
#[test]
#[cfg(feature = "safe_api")]
fn test_generate_variable() {
assert!($name::generate(0).is_err());
assert!($name::generate(usize::max_value()).is_err());
assert!($name::generate(1).is_ok());
assert!($name::generate(64).is_ok());
let test_zero = $name::from_slice(&[0u8; 128]).unwrap();
let test_rand = $name::generate(128).unwrap();
assert!(test_zero != test_rand);
assert!(test_rand.len() == 128);
}
));
macro_rules! construct_secret_key {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => (
$(#[$meta])*
pub struct $name {
value: [u8; $upper_bound],
original_length: usize,
}
impl_omitted_debug_trait!($name);
impl_drop_trait!($name);
impl_ct_partialeq_trait!($name, unprotected_as_bytes);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_unprotected_as_bytes!();
func_generate!($name, $upper_bound, $gen_length);
func_len!();
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_bound_parameters!($name, $lower_bound, $upper_bound, $gen_length);
test_from_slice!($name, $lower_bound, $upper_bound);
test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes);
test_partial_eq!($name, $upper_bound);
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod tests_with_std {
use super::*;
test_generate!($name, $gen_length);
test_omitted_debug!($name, $upper_bound);
}
}
);
}
macro_rules! construct_public {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => (
#[derive(Clone, Copy)]
$(#[$meta])*
pub struct $name {
value: [u8; $upper_bound],
original_length: usize,
}
impl_ct_partialeq_trait!($name, as_ref);
impl_normal_debug_trait!($name);
impl_asref_trait!($name);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_len!();
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound);
test_from_slice!($name, $lower_bound, $upper_bound);
test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref);
test_partial_eq!($name, $upper_bound);
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod tests_with_std {
use super::*;
test_normal_debug!($name, $upper_bound);
}
}
);
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr, $gen_length:expr)) => (
#[derive(Clone, Copy)]
$(#[$meta])*
pub struct $name {
value: [u8; $upper_bound],
original_length: usize,
}
impl_ct_partialeq_trait!($name, as_ref);
impl_normal_debug_trait!($name);
impl_asref_trait!($name);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_generate!($name, $upper_bound, $gen_length);
func_len!();
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound);
test_from_slice!($name, $lower_bound, $upper_bound);
test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, as_ref);
test_partial_eq!($name, $upper_bound);
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod tests_with_std {
use super::*;
test_normal_debug!($name, $upper_bound);
test_generate!($name, $gen_length);
}
}
);
}
macro_rules! construct_tag {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => (
#[derive(Clone, Copy)]
$(#[$meta])*
pub struct $name {
value: [u8; $upper_bound],
original_length: usize,
}
impl_omitted_debug_trait!($name);
impl_ct_partialeq_trait!($name, unprotected_as_bytes);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_unprotected_as_bytes!();
func_len!();
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_bound_parameters!($name, $lower_bound, $upper_bound, $upper_bound);
test_from_slice!($name, $lower_bound, $upper_bound);
test_as_bytes_and_get_length!($name, $lower_bound, $upper_bound, unprotected_as_bytes);
test_partial_eq!($name, $upper_bound);
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod tests_with_std {
use super::*;
test_omitted_debug!($name, $upper_bound);
}
}
);
}
macro_rules! construct_hmac_key {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $size:expr)) => (
$(#[$meta])*
pub struct $name {
value: [u8; $size],
original_length: usize,
}
impl_omitted_debug_trait!($name);
impl_drop_trait!($name);
impl_ct_partialeq_trait!($name, unprotected_as_bytes);
impl $name {
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> {
use crate::hazardous::hash::sha512::{Sha512, SHA512_OUTSIZE};
let mut secret_key = [0u8; $size];
let slice_len = slice.len();
if slice_len > $size {
secret_key[..SHA512_OUTSIZE].copy_from_slice(&Sha512::digest(slice)?.as_ref());
} else {
secret_key[..slice_len].copy_from_slice(slice);
}
Ok($name { value: secret_key, original_length: $size })
}
func_unprotected_as_bytes!();
func_generate!($name, $size, $size);
func_len!();
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_as_bytes_and_get_length!($name, $size, $size, unprotected_as_bytes);
test_partial_eq!($name, $size);
#[test]
fn test_key_size() {
assert!($name::from_slice(&[0u8; $size]).is_ok());
assert!($name::from_slice(&[0u8; $size - $size]).is_ok());
assert!($name::from_slice(&[0u8; $size + 1]).is_ok());
}
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod tests_with_std {
use super::*;
test_generate!($name, $size);
test_omitted_debug!($name, $size);
}
}
);
}
#[cfg(feature = "safe_api")]
macro_rules! construct_secret_key_variable_size {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $default_size:expr)) => (
#[cfg(feature = "safe_api")]
$(#[$meta])*
pub struct $name {
pub(crate) value: Vec<u8>,
original_length: usize,
}
impl_omitted_debug_trait!($name);
impl_drop_trait!($name);
impl_ct_partialeq_trait!($name, unprotected_as_bytes);
impl_default_trait!($name, $default_size);
impl $name {
func_from_slice_variable_size!($name);
func_unprotected_as_bytes!();
func_len!();
func_generate_variable_size!($name);
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_from_slice_variable!($name);
test_as_bytes_and_get_length!($name, 1, $default_size + 1, unprotected_as_bytes);
test_generate_variable!($name);
test_omitted_debug!($name, $default_size);
test_partial_eq!($name, $default_size);
}
);
}
#[cfg(feature = "safe_api")]
macro_rules! construct_salt_variable_size {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $default_size:expr)) => (
#[cfg(feature = "safe_api")]
$(#[$meta])*
pub struct $name {
value: Vec<u8>,
original_length: usize,
}
impl_normal_debug_trait!($name);
impl_default_trait!($name, $default_size);
impl_ct_partialeq_trait!($name, as_ref);
impl_asref_trait!($name);
impl $name {
func_from_slice_variable_size!($name);
func_len!();
func_generate_variable_size!($name);
}
#[cfg(test)]
mod $test_module_name {
use super::*;
test_from_slice_variable!($name);
test_as_bytes_and_get_length!($name, 1, $default_size + 1, as_ref);
test_generate_variable!($name);
test_partial_eq!($name, $default_size);
test_normal_debug!($name, $default_size);
}
);
}