#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(feature = "std"))]
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(feature = "std")]
use std::time::Duration;
#[cfg(not(feature = "std"))]
use core::time::Duration;
use ::core::marker::PhantomData;
use rand::{CryptoRng, RngCore};
use crate::error::{Error, Result};
use crate::hash::HashFunction;
use crate::types::Salt;
use zeroize::Zeroize;
pub mod common;
pub mod params;
#[cfg(feature = "alloc")]
pub mod hkdf;
#[cfg(feature = "alloc")]
pub mod pbkdf2;
#[cfg(feature = "alloc")]
pub mod argon2;
pub use common::SecurityLevel;
pub use params::{ParamProvider, PasswordHash};
#[cfg(feature = "alloc")]
pub use hkdf::Hkdf;
#[cfg(feature = "alloc")]
pub use pbkdf2::{Pbkdf2, Pbkdf2Params};
#[cfg(feature = "alloc")]
pub use argon2::{Algorithm as Argon2Type, Argon2, Params as Argon2Params};
pub trait KdfAlgorithm {
const MIN_SALT_SIZE: usize;
const DEFAULT_OUTPUT_SIZE: usize;
const ALGORITHM_ID: &'static str;
fn name() -> String {
Self::ALGORITHM_ID.to_string()
}
fn security_level() -> SecurityLevel;
}
pub trait KdfOperation<'a, A: KdfAlgorithm, T = Vec<u8>>: Sized {
fn with_ikm(self, ikm: &'a [u8]) -> Self;
fn with_salt(self, salt: &'a [u8]) -> Self;
fn with_info(self, info: &'a [u8]) -> Self;
fn with_output_length(self, length: usize) -> Self;
fn derive(self) -> Result<T>;
fn derive_array<const N: usize>(self) -> Result<[u8; N]>;
}
pub trait KeyDerivationFunction {
type Algorithm: KdfAlgorithm;
type Salt: AsRef<[u8]> + AsMut<[u8]> + Clone;
fn new() -> Self;
#[cfg(feature = "alloc")]
fn derive_key(
&self,
input: &[u8],
salt: Option<&[u8]>,
info: Option<&[u8]>,
length: usize,
) -> Result<Vec<u8>>;
fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm>
where
Self: Sized;
fn security_level() -> SecurityLevel {
Self::Algorithm::security_level()
}
fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Salt;
}
pub enum HkdfAlgorithm<H: HashFunction> {
_Hash(PhantomData<H>),
}
impl<H: HashFunction> KdfAlgorithm for HkdfAlgorithm<H> {
const MIN_SALT_SIZE: usize = 16;
const DEFAULT_OUTPUT_SIZE: usize = 32;
const ALGORITHM_ID: &'static str = "HKDF";
fn name() -> String {
format!("{}-{}", Self::ALGORITHM_ID, H::name())
}
fn security_level() -> SecurityLevel {
match H::output_size() * 8 {
bits if bits >= 512 => SecurityLevel::L256,
bits if bits >= 384 => SecurityLevel::L192,
bits if bits >= 256 => SecurityLevel::L128,
bits => SecurityLevel::Custom(bits as u32 / 2),
}
}
}
#[cfg(feature = "alloc")]
pub struct TypedHkdf<H: HashFunction + Clone> {
inner: hkdf::Hkdf<H, 16>, _phantom: PhantomData<H>,
}
#[cfg(feature = "alloc")]
impl<H: HashFunction + Clone> KeyDerivationFunction for TypedHkdf<H> {
type Algorithm = HkdfAlgorithm<H>;
type Salt = Salt<16>;
fn new() -> Self {
Self {
inner: hkdf::Hkdf::new(),
_phantom: PhantomData,
}
}
#[cfg(feature = "alloc")]
fn derive_key(
&self,
input: &[u8],
salt: Option<&[u8]>,
info: Option<&[u8]>,
length: usize,
) -> Result<Vec<u8>> {
self.inner.derive_key(input, salt, info, length)
}
fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm> {
HKdfOperation {
kdf: self,
ikm: None,
salt: None,
info: None,
length: Self::Algorithm::DEFAULT_OUTPUT_SIZE,
}
}
fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Salt {
Salt::random_with_size(rng, Self::Algorithm::MIN_SALT_SIZE).expect("Salt generation failed")
}
}
#[cfg(feature = "alloc")]
pub struct HKdfOperation<'a, H: HashFunction + Clone> {
kdf: &'a TypedHkdf<H>,
ikm: Option<&'a [u8]>,
salt: Option<&'a [u8]>,
info: Option<&'a [u8]>,
length: usize,
}
#[cfg(feature = "alloc")]
impl<'a, H: HashFunction + Clone> KdfOperation<'a, HkdfAlgorithm<H>> for HKdfOperation<'a, H> {
fn with_ikm(mut self, ikm: &'a [u8]) -> Self {
self.ikm = Some(ikm);
self
}
fn with_salt(mut self, salt: &'a [u8]) -> Self {
self.salt = Some(salt);
self
}
fn with_info(mut self, info: &'a [u8]) -> Self {
self.info = Some(info);
self
}
fn with_output_length(mut self, length: usize) -> Self {
self.length = length;
self
}
fn derive(self) -> Result<Vec<u8>> {
let ikm = self
.ikm
.ok_or_else(|| Error::param("ikm", "Input keying material is required"))?;
self.kdf.derive_key(ikm, self.salt, self.info, self.length)
}
fn derive_array<const N: usize>(self) -> Result<[u8; N]> {
if self.length != N {
return Err(Error::Length {
context: "HKDF output",
expected: N,
actual: self.length,
});
}
let vec = self.derive()?;
let mut array = [0u8; N];
array.copy_from_slice(&vec);
Ok(array)
}
}
pub enum Pbkdf2Algorithm<H: HashFunction> {
_Hash(PhantomData<H>),
}
impl<H: HashFunction> KdfAlgorithm for Pbkdf2Algorithm<H> {
const MIN_SALT_SIZE: usize = 16;
const DEFAULT_OUTPUT_SIZE: usize = 32;
const ALGORITHM_ID: &'static str = "PBKDF2";
fn name() -> String {
format!("{}-{}", Self::ALGORITHM_ID, H::name())
}
fn security_level() -> SecurityLevel {
match H::output_size() * 8 {
bits if bits >= 512 => SecurityLevel::L128, bits if bits >= 384 => SecurityLevel::L128,
bits if bits >= 256 => SecurityLevel::L128,
bits => SecurityLevel::Custom(bits as u32 / 2),
}
}
}
#[cfg(feature = "alloc")]
pub struct TypedPbkdf2<H: HashFunction + Clone> {
inner: pbkdf2::Pbkdf2<H, 16>, _phantom: PhantomData<H>,
}
#[cfg(feature = "alloc")]
impl<H: HashFunction + Clone> KeyDerivationFunction for TypedPbkdf2<H> {
type Algorithm = Pbkdf2Algorithm<H>;
type Salt = Salt<16>;
fn new() -> Self {
Self {
inner: pbkdf2::Pbkdf2::new(),
_phantom: PhantomData,
}
}
#[cfg(feature = "alloc")]
fn derive_key(
&self,
input: &[u8],
salt: Option<&[u8]>,
info: Option<&[u8]>,
length: usize,
) -> Result<Vec<u8>> {
self.inner.derive_key(input, salt, info, length)
}
fn builder(&self) -> impl KdfOperation<'_, Self::Algorithm> {
Pbkdf2Builder {
kdf: self,
password: None,
salt: None,
iterations: 600_000, length: Self::Algorithm::DEFAULT_OUTPUT_SIZE,
}
}
fn generate_salt<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Salt {
Salt::random_with_size(rng, Self::Algorithm::MIN_SALT_SIZE).expect("Salt generation failed")
}
}
#[cfg(feature = "alloc")]
pub struct Pbkdf2Builder<'a, H: HashFunction + Clone> {
kdf: &'a TypedPbkdf2<H>,
password: Option<&'a [u8]>,
salt: Option<&'a [u8]>,
iterations: u32,
length: usize,
}
#[cfg(feature = "alloc")]
impl<H: HashFunction + Clone> Pbkdf2Builder<'_, H> {
pub fn with_iterations(mut self, iterations: u32) -> Self {
self.iterations = iterations;
self
}
}
#[cfg(feature = "alloc")]
impl<'a, H: HashFunction + Clone> KdfOperation<'a, Pbkdf2Algorithm<H>> for Pbkdf2Builder<'a, H> {
fn with_ikm(mut self, password: &'a [u8]) -> Self {
self.password = Some(password);
self
}
fn with_salt(mut self, salt: &'a [u8]) -> Self {
self.salt = Some(salt);
self
}
fn with_info(self, _info: &'a [u8]) -> Self {
self
}
fn with_output_length(mut self, length: usize) -> Self {
self.length = length;
self
}
fn derive(self) -> Result<Vec<u8>> {
let password = self
.password
.ok_or_else(|| Error::param("password", "Password is required"))?;
let salt = self
.salt
.ok_or_else(|| Error::param("salt", "Salt is required"))?;
let mut params = self.kdf.inner.params().clone();
params.iterations = self.iterations;
params.key_length = self.length;
let mut kdf = self.kdf.inner.clone();
kdf.set_params(params);
kdf.derive_key(password, Some(salt), None, self.length)
}
fn derive_array<const N: usize>(self) -> Result<[u8; N]> {
if self.length != N {
return Err(Error::Length {
context: "PBKDF2 output",
expected: N,
actual: self.length,
});
}
let vec = self.derive()?;
let mut array = [0u8; N];
array.copy_from_slice(&vec);
Ok(array)
}
}
pub trait PasswordHashFunction: KeyDerivationFunction + ParamProvider {
type Password: AsRef<[u8]> + AsMut<[u8]> + Clone + Zeroize;
fn hash_password(&self, password: &Self::Password) -> Result<PasswordHash>;
fn verify(&self, password: &Self::Password, hash: &PasswordHash) -> Result<bool>;
fn benchmark(&self) -> Duration;
fn recommended_params(target_duration: Duration) -> Self::Params;
}