use crate::error::{CryptoError, Result};
use crate::internal::zeroize::Zeroize;
#[derive(Clone)]
pub struct Seed([u8; Self::LEN]);
impl Seed {
pub const LEN: usize = 32;
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != Self::LEN {
return Err(CryptoError::InvalidKeyLength {
algorithm: "Seed",
expected: Self::LEN,
got: slice.len(),
});
}
let mut arr = [0u8; Self::LEN];
arr.copy_from_slice(slice);
Ok(Self(arr))
}
pub fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
pub fn into_bytes(self) -> [u8; Self::LEN] {
self.0
}
}
impl From<[u8; 32]> for Seed {
fn from(bytes: [u8; 32]) -> Self {
Self(bytes)
}
}
impl AsRef<[u8]> for Seed {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Drop for Seed {
fn drop(&mut self) {
self.0.zeroize();
}
}
impl std::fmt::Debug for Seed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Seed")
.field("len", &Self::LEN)
.finish_non_exhaustive()
}
}
#[derive(Clone, Debug)]
pub struct XNonce([u8; Self::LEN]);
impl XNonce {
pub const LEN: usize = 24;
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
pub fn random() -> Self {
let mut bytes = [0u8; Self::LEN];
crate::internal::getrandom::fill(&mut bytes)
.expect("failed to generate random nonce");
Self(bytes)
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != Self::LEN {
return Err(CryptoError::InvalidNonceLength {
algorithm: "XChaCha20-Poly1305",
expected: Self::LEN,
got: slice.len(),
});
}
let mut arr = [0u8; Self::LEN];
arr.copy_from_slice(slice);
Ok(Self(arr))
}
pub fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
}
impl From<[u8; 24]> for XNonce {
fn from(bytes: [u8; 24]) -> Self {
Self(bytes)
}
}
impl AsRef<[u8]> for XNonce {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Clone, Debug)]
pub struct CNonce([u8; Self::LEN]);
impl CNonce {
pub const LEN: usize = 12;
pub const fn new(bytes: [u8; Self::LEN]) -> Self {
Self(bytes)
}
pub fn random() -> Self {
let mut bytes = [0u8; Self::LEN];
crate::internal::getrandom::fill(&mut bytes)
.expect("failed to generate random nonce");
Self(bytes)
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() != Self::LEN {
return Err(CryptoError::InvalidNonceLength {
algorithm: "ChaCha20",
expected: Self::LEN,
got: slice.len(),
});
}
let mut arr = [0u8; Self::LEN];
arr.copy_from_slice(slice);
Ok(Self(arr))
}
pub fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.0
}
}
impl From<[u8; 12]> for CNonce {
fn from(bytes: [u8; 12]) -> Self {
Self(bytes)
}
}
impl AsRef<[u8]> for CNonce {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_seed_roundtrip() {
let raw = [0xABu8; 32];
let seed = Seed::new(raw);
assert_eq!(seed.as_bytes(), &raw);
let back = seed.into_bytes();
assert_eq!(back, raw);
}
#[test]
fn test_seed_from_slice_wrong_len() {
let short = [0u8; 16];
let err = Seed::from_slice(&short).unwrap_err();
assert!(matches!(err, CryptoError::InvalidKeyLength { expected: 32, got: 16, .. }));
}
#[test]
fn test_seed_debug_no_leak() {
let seed = Seed::new([0x42u8; 32]);
let dbg = format!("{:?}", seed);
assert!(!dbg.contains("42"));
}
#[test]
fn test_xnonce_random_unique() {
let a = XNonce::random();
let b = XNonce::random();
assert_ne!(a.as_bytes(), b.as_bytes());
}
#[test]
fn test_cnonce_from_slice_wrong_len() {
let short = [0u8; 8];
let err = CNonce::from_slice(&short).unwrap_err();
assert!(matches!(err, CryptoError::InvalidNonceLength { expected: 12, got: 8, .. }));
}
#[test]
fn test_xnonce_from_slice_correct() {
let raw = [0x99u8; 24];
let nonce = XNonce::from_slice(&raw).unwrap();
assert_eq!(nonce.as_bytes(), &raw);
}
#[test]
fn test_cnonce_random_unique() {
let a = CNonce::random();
let b = CNonce::random();
assert_ne!(a.as_bytes(), b.as_bytes());
}
}