#[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, 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[..])
}
}
));
#[cfg(feature = "serde")]
macro_rules! impl_serde_traits (($name:ident, $bytes_function:ident) => (
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let bytes: &[u8] = &self.$bytes_function();
bytes.serialize(serializer)
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bytes = Vec::<u8>::deserialize(deserializer)?;
std::convert::TryFrom::try_from(bytes.as_slice()).map_err(serde::de::Error::custom)
}
}
));
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! impl_try_from_trait (($name:ident) => (
impl core::convert::TryFrom<&[u8]> for $name {
type Error = UnknownCryptoError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
Self::from_slice(slice)
}
}
));
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."]
pub fn from_slice(slice: &[u8]) -> Result<$name, UnknownCryptoError> {
let slice_len = slice.len();
if !($lower_bound..=$upper_bound).contains(&slice_len) {
return Err(UnknownCryptoError);
}
let mut value = [0u8; $upper_bound];
value[..slice_len].copy_from_slice(slice);
Ok($name { 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() || slice.len() > (isize::MAX as usize) {
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_is_empty (() => (
#[inline]
pub fn is_empty(&self) -> bool {
self.original_length == 0
}
));
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, 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 > (isize::MAX as usize) {
return Err(UnknownCryptoError);
}
let mut value = vec![0u8; length];
crate::util::secure_rand_bytes(&mut value).unwrap();
Ok($name { value, original_length: length })
}
));
#[cfg(test)]
#[cfg(feature = "serde")]
macro_rules! test_serde_impls (($name:ident, $gen_length:expr) => (
#[test]
fn test_serde_serialized_equivalence_to_bytes_fn() {
let bytes = &[38u8; $gen_length][..];
let orion_type = $name::from_slice(bytes).unwrap();
let serialized_from_bytes = serde_json::to_value(bytes).unwrap();
let serialized_from_orion_type = serde_json::to_value(&orion_type).unwrap();
assert_eq!(serialized_from_bytes, serialized_from_orion_type);
}
#[test]
fn test_serde_deserialized_equivalence_to_bytes_fn() {
let bytes = &[38u8; $gen_length][..];
let serialized_from_bytes = serde_json::to_value(bytes).unwrap();
let orion_type: $name = serde_json::from_value(serialized_from_bytes).unwrap();
assert_eq!(orion_type, bytes);
}
));
#[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_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[0u8; $upper_bound]).unwrap());
assert_ne!($name::from_slice(&[0u8; $upper_bound]).unwrap(), $name::from_slice(&[1u8; $upper_bound]).unwrap());
assert_eq!($name::from_slice(&[0u8; $upper_bound]).unwrap(), [0u8; $upper_bound].as_ref());
assert_ne!($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_eq!(test_upper.$bytes_function().len(), test_upper.len());
assert_eq!(test_upper.len(), $upper_bound);
assert_eq!(test_lower.$bytes_function().len(), test_lower.len());
assert_eq!(test_lower.len(), $lower_bound);
assert_eq!(test_upper.is_empty(), false);
assert_eq!(test_lower.is_empty(), false);
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_eq!(test_upper.$bytes_function().len(), test_upper.len());
assert_eq!(test_upper.len(), $upper_bound - 1);
assert_eq!(test_lower.$bytes_function().len(), test_lower.len());
assert_eq!(test_lower.len(), $lower_bound + 1);
assert_eq!(test_upper.is_empty(), false);
assert_eq!(test_lower.is_empty(), false);
}
}
));
#[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_ne!(test_zero, test_rand);
assert_eq!(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((isize::MAX as usize) + 1).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_ne!(test_zero, test_rand);
assert_eq!(test_rand.len(), 128);
}
));
macro_rules! construct_secret_key {
($(#[$meta:meta])*
($name:ident, $test_module_name:ident, $lower_bound:expr, $upper_bound:expr)) => (
$(#[$meta])*
///
/// # Security:
/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections
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_len!();
func_is_empty!();
}
#[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);
}
}
);
($(#[$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!();
func_is_empty!();
}
#[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 {
pub(crate) value: [u8; $upper_bound],
original_length: usize,
}
impl_ct_partialeq_trait!($name, as_ref);
impl_normal_debug_trait!($name);
impl_try_from_trait!($name);
impl_asref_trait!($name);
#[cfg(feature = "serde")]
impl_serde_traits!($name, as_ref);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_len!();
func_is_empty!();
}
#[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(feature = "serde")]
test_serde_impls!($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 {
pub(crate) value: [u8; $upper_bound],
original_length: usize,
}
impl_ct_partialeq_trait!($name, as_ref);
impl_normal_debug_trait!($name);
impl_try_from_trait!($name);
impl_asref_trait!($name);
#[cfg(feature = "serde")]
impl_serde_traits!($name, as_ref);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_generate!($name, $upper_bound, $gen_length);
func_len!();
func_is_empty!();
}
#[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(feature = "serde")]
test_serde_impls!($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_try_from_trait!($name);
#[cfg(feature = "serde")]
impl_serde_traits!($name, unprotected_as_bytes);
impl $name {
func_from_slice!($name, $lower_bound, $upper_bound);
func_unprotected_as_bytes!();
func_len!();
func_is_empty!();
}
#[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(feature = "serde")]
test_serde_impls!($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, $sha2:ident, $sha2_outsize:expr, $test_module_name:ident, $size:expr)) => (
$(#[$meta])*
///
/// # Security:
/// - __**Avoid using**__ `unprotected_as_bytes()` whenever possible, as it breaks all protections
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> {
let mut secret_key = [0u8; $size];
let slice_len = slice.len();
if slice_len > $size {
secret_key[..$sha2_outsize].copy_from_slice(&$sha2::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!();
func_is_empty!();
}
#[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_is_empty!();
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_try_from_trait!($name);
#[cfg(feature = "serde")]
impl_serde_traits!($name, as_ref);
impl $name {
func_from_slice_variable_size!($name);
func_len!();
func_is_empty!();
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);
#[cfg(feature = "serde")]
test_serde_impls!($name, $default_size);
}
);
}