use std::{fmt::Debug, hash::Hash};
use zeroize::ZeroizeOnDrop;
use crate::{CryptoKey, PrivateKey, SigningKey, SymmetricCryptoKey};
pub trait KeySlotId:
Debug + Clone + Copy + Hash + Eq + PartialEq + Ord + PartialOrd + Send + Sync + 'static
{
#[allow(missing_docs)]
type KeyValue: CryptoKey + Send + Sync + ZeroizeOnDrop;
fn is_local(&self) -> bool;
fn new_local(id: LocalId) -> Self;
}
pub trait KeySlotIds {
#[allow(missing_docs)]
type Symmetric: KeySlotId<KeyValue = SymmetricCryptoKey>;
#[allow(missing_docs)]
type Private: KeySlotId<KeyValue = PrivateKey>;
type Signing: KeySlotId<KeyValue = SigningKey>;
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct LocalId(pub(crate) uuid::Uuid);
impl LocalId {
pub(crate) fn new() -> Self {
LocalId(uuid::Uuid::new_v4())
}
}
#[macro_export]
macro_rules! key_slot_ids {
( $(
#[$meta_type:tt]
$vis:vis enum $name:ident {
$(
$( #[$variant_tag:tt] )?
$variant:ident $( ( $inner:ty ) )?
),*
$(,)?
}
)+
$ids_vis:vis $ids_name:ident => $symm_name:ident, $private_name:ident, $signing_name:ident;
) => {
use $crate::LocalId;
$(
#[must_use]
#[derive(std::fmt::Debug, Clone, Copy, std::hash::Hash, Eq, PartialEq, Ord, PartialOrd)]
#[allow(missing_docs)]
$vis enum $name { $(
$variant $( ($inner) )?,
)* }
impl $crate::KeySlotId for $name {
type KeyValue = key_slot_ids!(@key_type $meta_type);
fn is_local(&self) -> bool {
use $name::*;
match self { $(
key_slot_ids!(@variant_match $variant $( ( $inner ) )?) =>
key_slot_ids!(@variant_value $( $variant_tag )? ),
)* }
}
fn new_local(id: LocalId) -> Self {
$(
{ key_slot_ids!(@new_local $variant id $( $variant_tag )? ) }
)*
}
}
)+
#[allow(missing_docs)]
$ids_vis struct $ids_name;
impl $crate::KeySlotIds for $ids_name {
type Symmetric = $symm_name;
type Private = $private_name;
type Signing = $signing_name;
}
};
( @key_type symmetric ) => { $crate::SymmetricCryptoKey };
( @key_type private ) => { $crate::PrivateKey };
( @key_type signing ) => { $crate::SigningKey };
( @variant_match $variant:ident ( $inner:ty ) ) => { $variant (_) };
( @variant_match $variant:ident ) => { $variant };
( @variant_value local ) => { true };
( @variant_value ) => { false };
( @new_local $variant:ident $id:ident local ) => { Self::$variant($id) };
( @new_local $variant:ident $id:ident ) => {{}};
}
#[cfg(test)]
pub(crate) mod tests {
use crate::{
KeySlotId, LocalId,
traits::tests::{TestPrivateKey, TestSigningKey, TestSymmKey},
};
#[test]
fn test_local() {
let local = LocalId::new();
assert!(!TestSymmKey::A(0).is_local());
assert!(!TestSymmKey::B((4, 10)).is_local());
assert!(TestSymmKey::C(local).is_local());
assert!(!TestPrivateKey::A(0).is_local());
assert!(!TestPrivateKey::B.is_local());
assert!(TestPrivateKey::C(local).is_local());
assert!(!TestSigningKey::A(0).is_local());
assert!(!TestSigningKey::B.is_local());
assert!(TestSigningKey::C(local).is_local());
}
}