use crate::core::PasetoError;
use ring::rand::{SecureRandom, SystemRandom};
use std::convert::{From, TryFrom};
use std::fmt::Debug;
use std::ops::Deref;
use zeroize::Zeroize;
#[derive(Zeroize)]
#[zeroize(drop)]
#[derive(Clone)]
pub struct Key<const KEYSIZE: usize>([u8; KEYSIZE]);
impl<const KEYSIZE: usize> Default for Key<KEYSIZE> {
fn default() -> Self {
Self([0u8; KEYSIZE])
}
}
impl<const KEYSIZE: usize> AsRef<[u8]> for Key<KEYSIZE> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<const KEYSIZE: usize> Deref for Key<KEYSIZE> {
type Target = [u8; KEYSIZE];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const KEYSIZE: usize> TryFrom<&[u8]> for Key<KEYSIZE> {
type Error = PasetoError;
fn try_from(key: &[u8]) -> Result<Self, Self::Error> {
if key.len() != KEYSIZE {
return Err(PasetoError::InvalidKey);
}
let mut me = Self::default();
me.0.copy_from_slice(key);
Ok(me)
}
}
impl<const KEYSIZE: usize> From<&[u8; KEYSIZE]> for Key<KEYSIZE> {
fn from(key: &[u8; KEYSIZE]) -> Self {
Self(*key)
}
}
impl<const KEYSIZE: usize> From<[u8; KEYSIZE]> for Key<KEYSIZE> {
fn from(key: [u8; KEYSIZE]) -> Self {
Self(key)
}
}
impl<const KEYSIZE: usize> TryFrom<&str> for Key<KEYSIZE> {
type Error = PasetoError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let key = hex::decode(value).map_err(|_| PasetoError::InvalidKey)?;
if key.len() != KEYSIZE {
return Err(PasetoError::InvalidKey);
}
let mut me = Self::default();
me.0.copy_from_slice(&key);
Ok(me)
}
}
impl<const KEYSIZE: usize> Key<KEYSIZE> {
pub fn try_new_random() -> Result<Self, PasetoError> {
let rng = SystemRandom::new();
let mut buf = [0u8; KEYSIZE];
rng.fill(&mut buf)?;
Ok(Self(buf))
}
}
#[cfg(test)]
mod key_size_check_tests {
use super::*;
#[test]
fn try_from_rejects_short_slice() {
let too_short = [0u8; 16];
let result = Key::<32>::try_from(&too_short[..]);
assert!(matches!(result, Err(PasetoError::InvalidKey)));
}
#[test]
fn try_from_rejects_long_slice() {
let too_long = [0u8; 48];
let result = Key::<32>::try_from(&too_long[..]);
assert!(matches!(result, Err(PasetoError::InvalidKey)));
}
#[test]
fn try_from_accepts_exact_size() {
let exact = [0u8; 32];
let result = Key::<32>::try_from(&exact[..]);
assert!(result.is_ok());
}
}
impl<const KEYSIZE: usize> Debug for Key<KEYSIZE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "!!! KEY IS PRIVATE !!!")
}
}