use core::fmt;
use zeroize::Zeroize;
use crate::backends::ProviderRng;
use crate::{Error, Provider, Result};
pub struct Key {
bytes: [u8; Self::LEN],
provider: Option<Provider>,
}
impl Key {
pub const LEN: usize = 32;
pub fn generate() -> Result<Self> {
let provider = Provider::build_default().ok_or(Error::ProviderSelectionRequired)?;
Self::generate_with_provider(provider)
}
pub fn generate_with_provider(provider: Provider) -> Result<Self> {
let mut rng = ProviderRng::new(provider);
let mut key = [0u8; Self::LEN];
rng.generate_key(&mut key)?;
Ok(Self {
bytes: key,
provider: Some(provider),
})
}
#[must_use]
pub const fn from_bytes(bytes: [u8; Self::LEN]) -> Self {
Self {
bytes,
provider: None,
}
}
#[must_use]
pub const fn from_bytes_with_provider(bytes: [u8; Self::LEN], provider: Provider) -> Self {
Self {
bytes,
provider: Some(provider),
}
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
&self.bytes
}
pub const fn provider(&self) -> Result<Provider> {
match self.provider {
Some(provider) => Ok(provider),
None => match Provider::build_default() {
Some(provider) => Ok(provider),
None => Err(Error::ProviderSelectionRequired),
},
}
}
}
impl Clone for Key {
fn clone(&self) -> Self {
Self {
bytes: self.bytes,
provider: self.provider,
}
}
}
impl Drop for Key {
fn drop(&mut self) {
self.bytes.zeroize();
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Key").field(&"[REDACTED]").finish()
}
}
impl From<[u8; Key::LEN]> for Key {
fn from(bytes: [u8; Key::LEN]) -> Self {
Self::from_bytes(bytes)
}
}
impl TryFrom<&[u8]> for Key {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
let actual = bytes.len();
let bytes = bytes
.try_into()
.map_err(|_| Error::InvalidKeyLength { actual })?;
Ok(Self::from_bytes(bytes))
}
}
#[cfg(test)]
pub(crate) fn test_key() -> Key {
Key::from_bytes_with_provider([0; Key::LEN], Provider::COMPILED[0])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Parameters, decrypt, encrypt};
#[test]
fn implicit_keys_resolve_only_for_single_provider_builds() {
let key = Key::from_bytes([0x21; Key::LEN]);
if let Some(provider) = Provider::build_default() {
assert_eq!(key.provider(), Ok(provider));
assert_eq!(Key::generate().unwrap().provider(), Ok(provider));
let ciphertext = encrypt(
&key,
b"implicit provider",
Parameters::SEGMENT_4_KIB,
b"message",
)
.unwrap();
assert_eq!(
decrypt(&key, b"implicit provider", &ciphertext).unwrap(),
b"message"
);
} else {
assert_eq!(key.provider(), Err(Error::ProviderSelectionRequired));
assert!(matches!(
Key::generate(),
Err(Error::ProviderSelectionRequired)
));
}
}
#[test]
fn generated_keys_have_required_size() {
for &provider in Provider::COMPILED {
assert_eq!(
Key::generate_with_provider(provider)
.unwrap()
.as_bytes()
.len(),
Key::LEN
);
}
}
#[test]
fn key_debug_is_redacted() {
let key = Key::from_bytes([0xAB; Key::LEN]);
let formatted = format!("{key:?}");
assert!(formatted.contains("REDACTED"));
assert!(!formatted.contains("ab"));
assert!(!formatted.contains("AB"));
assert!(!formatted.contains("171"));
}
#[test]
fn key_from_array_preserves_bytes() {
let bytes = [0x42; Key::LEN];
let key = Key::from(bytes);
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn key_try_from_slice_accepts_exact_length() {
let bytes = [0x42; Key::LEN];
let key = Key::try_from(&bytes[..]).unwrap();
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn key_try_from_slice_rejects_short_and_long_inputs() {
let bytes = [0x42; Key::LEN + 1];
for length in [Key::LEN - 1, Key::LEN + 1] {
assert!(matches!(
Key::try_from(&bytes[..length]),
Err(Error::InvalidKeyLength { actual }) if actual == length
));
}
}
}