#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
use core::fmt;
use digest::{generic_array::typenum::Unsigned, Digest, FixedOutputReset, Update};
#[cfg(feature = "std")]
extern crate std;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
NoSecret,
NoOutput,
CounterOverflow,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(match self {
Error::NoSecret => "Buffer for secret has zero length.",
Error::NoOutput => "Buffer for key has zero length.",
Error::CounterOverflow => "Requested key length is to big.",
})
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl ::std::error::Error for Error {}
pub fn derive_key_into<D>(secret: &[u8], other_info: &[u8], key: &mut [u8]) -> Result<(), Error>
where
D: Digest + FixedOutputReset,
{
if secret.is_empty() {
return Err(Error::NoSecret);
}
if key.is_empty() {
return Err(Error::NoOutput);
}
const OVERFLOW_IS_POSSIBLE: bool = core::mem::size_of::<usize>() > 4;
if OVERFLOW_IS_POSSIBLE && (key.len() >= D::OutputSize::USIZE * (u32::MAX as usize)) {
return Err(Error::CounterOverflow);
}
let mut digest = D::new();
let mut counter: u32 = 1;
for chunk in key.chunks_mut(D::OutputSize::USIZE) {
Update::update(&mut digest, &counter.to_be_bytes());
Update::update(&mut digest, secret);
Update::update(&mut digest, other_info);
chunk.copy_from_slice(&digest.finalize_reset()[..chunk.len()]);
counter += 1;
}
Ok(())
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn derive_key<D>(
secret: &[u8],
other_info: &[u8],
length: usize,
) -> Result<std::vec::Vec<u8>, Error>
where
D: Digest + FixedOutputReset,
{
let mut key = std::vec![0u8; length];
derive_key_into::<D>(secret, other_info, &mut key)?;
Ok(key)
}