use core::fmt::Debug;
use crate::error::{Error, ErrorCode};
use crate::tlv::{FromTLV, TLVElement, TLVTag, TLVWrite, ToTLV, TLV};
use crate::utils::init::{init, zeroed, Init, IntoFallibleInit};
pub const HASH_LEN: usize = 32;
pub const SHA1_HASH_LEN: usize = 20;
pub const HMAC_HASH_LEN: usize = HASH_LEN;
pub const EC_CANON_SCALAR_LEN: usize = 32;
pub const EC_CANON_POINT_LEN: usize = EC_CANON_SCALAR_LEN * 2 + 1;
pub const PKC_CANON_PUBLIC_KEY_LEN: usize = EC_CANON_POINT_LEN;
pub const PKC_CANON_SECRET_KEY_LEN: usize = EC_CANON_SCALAR_LEN;
pub const PKC_SIGNATURE_LEN: usize = PKC_CANON_SECRET_KEY_LEN * 2;
pub const PKC_SHARED_SECRET_LEN: usize = 32;
pub const UINT320_CANON_LEN: usize = 40;
pub const AEAD_CANON_KEY_LEN: usize = 16;
pub const AEAD_NONCE_LEN: usize = 13;
pub const AEAD_TAG_LEN: usize = 16;
macro_rules! canon {
($len:expr, $zero: ident, $name:ident, $name_ref:ident) => {
#[allow(unused)]
pub type $name = $crate::crypto::CryptoSensitive<$len>;
#[allow(unused)]
pub type $name_ref<'a> = $crate::crypto::CryptoSensitiveRef<'a, $len>;
#[allow(unused)]
pub const $zero: $name = $name::new();
};
}
pub(crate) use canon;
canon!(HASH_LEN, HASH_ZEROED, Hash, HashRef);
canon!(HMAC_HASH_LEN, HMAC_HASH_ZEROED, HmacHash, HmacHashRef);
canon!(
UINT320_CANON_LEN,
UINT320_ZEROED,
CanonUint320,
CanonUint320Ref
);
canon!(
AEAD_CANON_KEY_LEN,
AEAD_KEY_ZEROED,
CanonAeadKey,
CanonAeadKeyRef
);
canon!(AEAD_NONCE_LEN, AEAD_NONCE_ZEROED, AeadNonce, AeadNonceRef);
canon!(AEAD_TAG_LEN, AEAD_TAG_ZEROED, AeadTag, AeadTagRef);
canon!(
PKC_CANON_PUBLIC_KEY_LEN,
PKC_PUBLIC_KEY_ZEROED,
CanonPkcPublicKey,
CanonPkcPublicKeyRef
);
canon!(
PKC_CANON_SECRET_KEY_LEN,
PKC_SECRET_KEY_ZEROED,
CanonPkcSecretKey,
CanonPkcSecretKeyRef
);
canon!(
PKC_SIGNATURE_LEN,
PKC_SIGNATURE_ZEROED,
CanonPkcSignature,
CanonPkcSignatureRef
);
canon!(
PKC_SHARED_SECRET_LEN,
PKC_SHARED_SECRET_ZEROED,
CanonPkcSharedSecret,
CanonPkcSharedSecretRef
);
canon!(
EC_CANON_SCALAR_LEN,
EC_SCALAR_ZEROED,
CanonEcScalar,
CanonEcScalarRef
);
canon!(
EC_CANON_POINT_LEN,
EC_POINT_ZEROED,
CanonEcPoint,
CanonEcPointRef
);
#[derive(Clone)]
pub struct CryptoSensitive<const N: usize> {
data: [u8; N],
}
impl<const N: usize> CryptoSensitive<N> {
#[inline(always)]
pub const fn new() -> Self {
Self { data: [0u8; N] }
}
pub const fn new_from_ref(other: CryptoSensitiveRef<'_, N>) -> Self {
let mut this = Self::new();
this.load(other);
this
}
#[inline(always)]
pub fn init() -> impl Init<Self> {
init!(Self {
data <- zeroed(),
})
}
pub fn zeroize(&mut self) {
for b in &mut self.data {
*b = 0;
}
}
pub const fn reference(&self) -> CryptoSensitiveRef<'_, N> {
CryptoSensitiveRef::new(&self.data)
}
pub const fn load(&mut self, other: CryptoSensitiveRef<'_, N>) {
self.load_from_array(other.access());
}
pub const fn load_from_array(&mut self, data: &[u8; N]) {
self.data.copy_from_slice(data);
}
pub fn try_load_from_slice(&mut self, data: &[u8]) -> Result<(), Error> {
if data.len() != N {
return Err(ErrorCode::InvalidData.into());
}
self.data.copy_from_slice(data);
Ok(())
}
pub const fn access(&self) -> &[u8; N] {
&self.data
}
pub const fn access_mut(&mut self) -> &mut [u8; N] {
&mut self.data
}
}
impl<const N: usize> Drop for CryptoSensitive<N> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<const N: usize> Default for CryptoSensitive<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> Debug for CryptoSensitive<N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "CryptoSensitive<{}>(**hidden**)", N)
}
}
#[cfg(feature = "defmt")]
impl<const N: usize> defmt::Format for CryptoSensitive<N> {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "CryptoSensitive<{}>(**hidden**)", N);
}
}
impl<const N: usize> From<CryptoSensitiveRef<'_, N>> for CryptoSensitive<N> {
fn from(other: CryptoSensitiveRef<'_, N>) -> Self {
let mut material = CryptoSensitive::new();
material.load(other);
material
}
}
impl<const N: usize> From<&[u8; N]> for CryptoSensitive<N> {
fn from(data: &[u8; N]) -> Self {
let mut material = CryptoSensitive::new();
material.load_from_array(data);
material
}
}
impl<const N: usize> From<[u8; N]> for CryptoSensitive<N> {
fn from(data: [u8; N]) -> Self {
let mut material = CryptoSensitive::new();
material.load_from_array(&data);
material
}
}
impl<const N: usize> TryFrom<&[u8]> for CryptoSensitive<N> {
type Error = Error;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let mut material = CryptoSensitive::new();
material.try_load_from_slice(data)?;
Ok(material)
}
}
impl<'a, const N: usize> FromTLV<'a> for CryptoSensitive<N> {
fn from_tlv(element: &TLVElement<'a>) -> Result<Self, crate::error::Error> {
Ok(Self {
data: element
.str()?
.try_into()
.map_err(|_| ErrorCode::ConstraintError)?,
})
}
fn init_from_tlv(element: TLVElement<'a>) -> impl Init<Self, Error> {
Init::chain(Self::init().into_fallible(), move |this| {
let data = element.str()?;
if data.len() != N {
Err(ErrorCode::ConstraintError)?;
}
this.access_mut().copy_from_slice(data);
Ok(())
})
}
}
impl<const N: usize> ToTLV for CryptoSensitive<N> {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
tw.str(tag, &self.data)
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
TLV::str(tag, self.data.as_slice()).into_tlv_iter()
}
}
#[derive(Copy, Clone)]
pub struct CryptoSensitiveRef<'a, const N: usize> {
data: &'a [u8; N],
}
impl<'a, const N: usize> CryptoSensitiveRef<'a, N> {
#[inline(always)]
pub const fn new(material: &'a [u8; N]) -> Self {
Self { data: material }
}
#[inline(always)]
pub fn new_from_slice(material: &'a [u8]) -> Self {
assert_eq!(material.len(), N);
Self::new(Self::as_array(material).unwrap()) }
#[inline(always)]
pub fn try_new(material: &'a [u8]) -> Result<Self, Error> {
if material.len() != N {
Err(ErrorCode::InvalidData)?;
}
Ok(Self::new(Self::as_array(material).unwrap())) }
pub fn split<const M1: usize, const M2: usize>(
&self,
) -> (CryptoSensitiveRef<'a, M1>, CryptoSensitiveRef<'a, M2>) {
let (left, right) = self.data.split_at(M1);
(
CryptoSensitiveRef::new_from_slice(left),
CryptoSensitiveRef::new_from_slice(right),
)
}
pub const fn access(&self) -> &'a [u8; N] {
self.data
}
const fn as_array<const L: usize>(slice: &'a [u8]) -> Option<&'a [u8; L]> {
if slice.len() == L {
let ptr = slice.as_ptr() as *const [u8; L];
let me = unsafe { &*ptr };
Some(me)
} else {
None
}
}
}
impl<const N: usize> Debug for CryptoSensitiveRef<'_, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "CryptoSensitiveRef<{}>(**hidden**)", N)
}
}
#[cfg(feature = "defmt")]
impl<const N: usize> defmt::Format for CryptoSensitiveRef<'_, N> {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "CryptoSensitiveRef<{}>(**hidden**)", N);
}
}
impl<'a, const N: usize> From<&'a CryptoSensitive<N>> for CryptoSensitiveRef<'a, N> {
fn from(cs: &'a CryptoSensitive<N>) -> Self {
cs.reference()
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for CryptoSensitiveRef<'a, N> {
fn from(data: &'a [u8; N]) -> Self {
Self::new(data)
}
}
impl<'a, const N: usize> TryFrom<&'a [u8]> for CryptoSensitiveRef<'a, N> {
type Error = Error;
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
Self::try_new(data)
}
}