use core::{borrow::Borrow, fmt, iter::IntoIterator, mem, result::Result};
use ctutils::{Choice, CtEq};
use hybrid_array::{Array, ArraySize};
use typenum::{IsLess, U65536};
use zeroize::ZeroizeOnDrop;
use crate::{
csprng::{Csprng, Random},
import::{ExportError, Import},
kdf::{Expand, Kdf, KdfError, Prk},
};
pub trait SecretKey: Clone + CtEq + for<'a> Import<&'a [u8]> + Random + ZeroizeOnDrop {
type Size: ArraySize + 'static;
fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError>;
}
pub trait RawSecretBytes {
fn raw_secret_bytes(&self) -> &[u8];
}
impl<T: RawSecretBytes> RawSecretBytes for &T {
#[inline]
fn raw_secret_bytes(&self) -> &[u8] {
(**self).raw_secret_bytes()
}
}
impl RawSecretBytes for [u8] {
#[inline]
fn raw_secret_bytes(&self) -> &[u8] {
self
}
}
#[derive(Clone, Default, ZeroizeOnDrop)]
#[repr(transparent)]
pub struct SecretKeyBytes<N: ArraySize>(Array<u8, N>);
impl<N: ArraySize> fmt::Debug for SecretKeyBytes<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SecretKeyBytes").finish_non_exhaustive()
}
}
impl<N: ArraySize> SecretKeyBytes<N> {
pub const SIZE: usize = N::USIZE;
#[inline]
pub const fn new(secret: Array<u8, N>) -> Self {
Self(secret)
}
#[allow(clippy::len_without_is_empty)]
#[inline]
pub const fn len(&self) -> usize {
N::USIZE
}
pub(crate) fn as_array<const U: usize>(&self) -> &[u8; U]
where
N: ArraySize<ArrayType<u8> = [u8; U]>,
{
&(self.0).0
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub(crate) fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.0
}
#[inline]
pub fn into_bytes(mut self) -> Array<u8, N> {
mem::take(&mut self.0)
}
}
impl<N: ArraySize> CtEq for SecretKeyBytes<N> {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl<N: ArraySize> Random for SecretKeyBytes<N> {
fn random<R: Csprng>(rng: R) -> Self {
Self(Random::random(rng))
}
}
impl<N: ArraySize> Expand for SecretKeyBytes<N>
where
N: IsLess<U65536>,
{
type Size = N;
fn expand_multi<'a, K, I>(prk: &Prk<K::PrkSize>, info: I) -> Result<Self, KdfError>
where
K: Kdf,
I: IntoIterator<Item = &'a [u8]>,
I::IntoIter: Clone,
{
Ok(Self(Expand::expand_multi::<K, I>(prk, info)?))
}
}
impl<N: ArraySize> RawSecretBytes for SecretKeyBytes<N> {
#[inline]
fn raw_secret_bytes(&self) -> &[u8] {
self.as_bytes()
}
}
pub trait PublicKey: Clone + fmt::Debug + Eq + for<'a> Import<&'a [u8]> {
type Data: Borrow<[u8]> + Clone + Sized;
fn export(&self) -> Self::Data;
}
raw_key! {
pub RawKey,
}
#[macro_export]
macro_rules! raw_key {
() => {};
(
$(#[$meta:meta])*
$vis:vis $name:ident,
$($tail:tt)*
) => {
$(#[$meta])*
#[derive(::core::clone::Clone, $crate::zeroize::ZeroizeOnDrop)]
#[repr(transparent)]
$vis struct $name<N: $crate::hybrid_array::ArraySize>($crate::keys::SecretKeyBytes<N>);
impl<N: ::hybrid_array::ArraySize> $name<N> {
#[inline]
pub const fn new(key: $crate::keys::SecretKeyBytes<N>) -> Self {
Self(key)
}
#[allow(clippy::len_without_is_empty)]
#[inline]
pub const fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub const fn as_slice(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub const fn as_bytes(&self) -> &$crate::keys::SecretKeyBytes<N> {
&self.0
}
#[inline]
pub fn into_bytes(mut self) -> $crate::keys::SecretKeyBytes<N> {
::core::mem::take(&mut self.0)
}
}
impl<N: $crate::hybrid_array::ArraySize> $crate::keys::SecretKey for $name<N> {
type Size = N;
#[inline]
fn try_export_secret(&self) -> ::core::result::Result<
$crate::keys::SecretKeyBytes<Self::Size>,
$crate::import::ExportError,
> {
::core::result::Result::Ok(self.0.clone())
}
}
impl<N: $crate::hybrid_array::ArraySize> $crate::csprng::Random for $name<N> {
fn random<R: $crate::csprng::Csprng>(rng: R) -> Self {
let sk = <$crate::keys::SecretKeyBytes<N> as $crate::csprng::Random>::random(rng);
Self(sk)
}
}
impl<N: $crate::hybrid_array::ArraySize> $crate::keys::RawSecretBytes for $name<N> {
#[inline]
fn raw_secret_bytes(&self) -> &[u8] {
$crate::keys::RawSecretBytes::raw_secret_bytes(&self.0)
}
}
impl<N: $crate::hybrid_array::ArraySize> $crate::kdf::Expand for $name<N>
where
N: ::typenum::IsLess<::typenum::U65536>
{
type Size = N;
fn expand_multi<'a, K, I>(
prk: &$crate::kdf::Prk<K::PrkSize>,
info: I,
) -> ::core::result::Result<Self, $crate::kdf::KdfError>
where
K: $crate::kdf::Kdf,
I: ::core::iter::IntoIterator<Item = &'a [u8]>,
I::IntoIter: ::core::clone::Clone,
{
::core::result::Result::Ok(Self($crate::kdf::Expand::expand_multi::<K, I>(prk, info)?))
}
}
impl<N: $crate::hybrid_array::ArraySize> ::ctutils::CtEq for $name<N> {
#[inline]
fn ct_eq(&self, other: &Self) -> ::ctutils::Choice {
self.0.ct_eq(&other.0)
}
}
impl<N, const U: usize> $crate::import::Import<[u8; U]> for $name<N>
where
N: $crate::hybrid_array::ArraySize<ArrayType<u8> = [u8; U]>,
{
#[inline]
fn import(key: [u8; U]) -> ::core::result::Result<Self, $crate::import::ImportError> {
let sk = $crate::keys::SecretKeyBytes::new($crate::hybrid_array::Array(key));
::core::result::Result::Ok(Self(sk))
}
}
impl<N: $crate::hybrid_array::ArraySize> $crate::import::Import<&[u8]> for $name<N> {
#[inline]
fn import(data: &[u8]) -> ::core::result::Result<Self, $crate::import::ImportError> {
let bytes = $crate::import::Import::<_>::import(data)?;
let sk = $crate::keys::SecretKeyBytes::new(bytes);
::core::result::Result::Ok(Self(sk))
}
}
impl<N: ::hybrid_array::ArraySize> ::core::fmt::Debug for $name<N> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct(stringify!($name)).finish_non_exhaustive()
}
}
raw_key!{ $($tail)* }
};
}
pub(crate) use raw_key;
#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("invalid key length")]
pub struct InvalidKey;