use crypto_core::{CryptoError, KdfAlgorithm, KdfFailureKind, KdfProfile};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use crate::constants::{
PBKDF2_MAX_ITERATIONS, PBKDF2_MAX_PASSWORD_LENGTH, PBKDF2_MAX_SALT_LENGTH,
PBKDF2_MIN_PASSWORD_LENGTH, PBKDF2_MIN_SALT_LENGTH, PBKDF2_MODERN_MIN_ITERATIONS,
PBKDF2_STANDARD_MIN_ITERATIONS,
};
#[cfg(any(feature = "native", feature = "wasm"))]
use crate::constants::{PBKDF2_MAX_OUTPUT_LENGTH, PBKDF2_MIN_OUTPUT_LENGTH};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pbkdf2Prf {
HmacSha256,
HmacSha512,
}
impl Pbkdf2Prf {
pub(crate) fn profile(self) -> KdfProfile {
match self {
Pbkdf2Prf::HmacSha256 => KdfProfile::Pbkdf2HmacSha256,
Pbkdf2Prf::HmacSha512 => KdfProfile::Pbkdf2HmacSha512,
}
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct Pbkdf2Password {
bytes: Zeroizing<Vec<u8>>,
}
impl Pbkdf2Password {
pub fn from_slice(input: &[u8], prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
if !(PBKDF2_MIN_PASSWORD_LENGTH..=PBKDF2_MAX_PASSWORD_LENGTH).contains(&input.len()) {
return Err(kdf_error(prf, KdfFailureKind::InvalidSecretLength));
}
Ok(Self {
bytes: Zeroizing::new(input.to_vec()),
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct Pbkdf2Salt {
bytes: Zeroizing<Vec<u8>>,
}
impl Pbkdf2Salt {
pub fn from_slice(input: &[u8], prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
if !(PBKDF2_MIN_SALT_LENGTH..=PBKDF2_MAX_SALT_LENGTH).contains(&input.len()) {
return Err(kdf_error(prf, KdfFailureKind::InvalidSaltLength));
}
Ok(Self {
bytes: Zeroizing::new(input.to_vec()),
})
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Pbkdf2Iterations {
value: u32,
}
impl Pbkdf2Iterations {
pub fn from_u32(value: u32, prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
if !(PBKDF2_STANDARD_MIN_ITERATIONS..=PBKDF2_MAX_ITERATIONS).contains(&value) {
return Err(kdf_error(prf, KdfFailureKind::InvalidIterationCount));
}
Ok(Self { value })
}
pub fn from_u32_modern(value: u32, prf: Pbkdf2Prf) -> Result<Self, CryptoError> {
if !(PBKDF2_MODERN_MIN_ITERATIONS..=PBKDF2_MAX_ITERATIONS).contains(&value) {
return Err(kdf_error(prf, KdfFailureKind::InvalidIterationCount));
}
Ok(Self { value })
}
pub fn as_u32(self) -> u32 {
self.value
}
}
pub struct Pbkdf2Output {
bytes: Zeroizing<Vec<u8>>,
}
impl Zeroize for Pbkdf2Output {
fn zeroize(&mut self) {
self.bytes.zeroize();
}
}
impl ZeroizeOnDrop for Pbkdf2Output {}
impl Pbkdf2Output {
#[cfg(any(feature = "native", feature = "wasm"))]
pub(crate) fn from_zeroizing(bytes: Zeroizing<Vec<u8>>) -> Self {
Self { bytes }
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
pub fn into_zeroizing(self) -> Zeroizing<Vec<u8>> {
self.bytes
}
}
impl core::fmt::Debug for Pbkdf2Output {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Pbkdf2Output(len={})", self.len())
}
}
#[cfg(any(feature = "native", feature = "wasm"))]
pub(crate) fn validate_output_len(len: usize, prf: Pbkdf2Prf) -> Result<(), CryptoError> {
if !(PBKDF2_MIN_OUTPUT_LENGTH..=PBKDF2_MAX_OUTPUT_LENGTH).contains(&len) {
return Err(kdf_error(prf, KdfFailureKind::InvalidOutputLength));
}
Ok(())
}
pub(crate) fn kdf_error(prf: Pbkdf2Prf, kind: KdfFailureKind) -> CryptoError {
CryptoError::Kdf {
algorithm: KdfAlgorithm::Pbkdf2,
profile: prf.profile(),
kind,
}
}