#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_root_url = "https://docs.rs/hkdf/0.12.3"
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
pub use hmac;
use core::fmt;
use core::marker::PhantomData;
use hmac::digest::{
crypto_common::AlgorithmName, generic_array::typenum::Unsigned, Output, OutputSizeUser,
};
use hmac::{Hmac, SimpleHmac};
mod errors;
mod sealed;
pub use errors::{InvalidLength, InvalidPrkLength};
pub type SimpleHkdfExtract<H> = HkdfExtract<H, SimpleHmac<H>>;
pub type SimpleHkdf<H> = Hkdf<H, SimpleHmac<H>>;
#[derive(Clone)]
pub struct HkdfExtract<H, I = Hmac<H>>
where
H: OutputSizeUser,
I: HmacImpl<H>,
{
hmac: I,
_pd: PhantomData<H>,
}
impl<H, I> HkdfExtract<H, I>
where
H: OutputSizeUser,
I: HmacImpl<H>,
{
pub fn new(salt: Option<&[u8]>) -> Self {
let default_salt = Output::<H>::default();
let salt = salt.unwrap_or(&default_salt);
Self {
hmac: I::new_from_slice(salt),
_pd: PhantomData,
}
}
pub fn input_ikm(&mut self, ikm: &[u8]) {
self.hmac.update(ikm);
}
pub fn finalize(self) -> (Output<H>, Hkdf<H, I>) {
let prk = self.hmac.finalize();
let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct");
(prk, hkdf)
}
}
impl<H, I> fmt::Debug for HkdfExtract<H, I>
where
H: OutputSizeUser + AlgorithmName,
I: HmacImpl<H>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("HkdfExtract<")?;
<H as AlgorithmName>::write_alg_name(f)?;
f.write_str("> { ... }")
}
}
#[derive(Clone)]
pub struct Hkdf<H: OutputSizeUser, I: HmacImpl<H> = Hmac<H>> {
hmac: I::Core,
_pd: PhantomData<H>,
}
impl<H: OutputSizeUser, I: HmacImpl<H>> Hkdf<H, I> {
pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self {
let (_, hkdf) = Self::extract(salt, ikm);
hkdf
}
pub fn from_prk(prk: &[u8]) -> Result<Self, InvalidPrkLength> {
if prk.len() < <H as OutputSizeUser>::OutputSize::to_usize() {
return Err(InvalidPrkLength);
}
Ok(Self {
hmac: I::new_core(prk),
_pd: PhantomData,
})
}
pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output<H>, Self) {
let mut extract_ctx = HkdfExtract::new(salt);
extract_ctx.input_ikm(ikm);
extract_ctx.finalize()
}
pub fn expand_multi_info(
&self,
info_components: &[&[u8]],
okm: &mut [u8],
) -> Result<(), InvalidLength> {
let mut prev: Option<Output<H>> = None;
let chunk_len = <H as OutputSizeUser>::OutputSize::USIZE;
if okm.len() > chunk_len * 255 {
return Err(InvalidLength);
}
for (block_n, block) in okm.chunks_mut(chunk_len).enumerate() {
let mut hmac = I::from_core(&self.hmac);
if let Some(ref prev) = prev {
hmac.update(prev)
};
for info in info_components {
hmac.update(info);
}
hmac.update(&[block_n as u8 + 1]);
let output = hmac.finalize();
let block_len = block.len();
block.copy_from_slice(&output[..block_len]);
prev = Some(output);
}
Ok(())
}
pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> {
self.expand_multi_info(&[info], okm)
}
}
impl<H, I> fmt::Debug for Hkdf<H, I>
where
H: OutputSizeUser + AlgorithmName,
I: HmacImpl<H>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Hkdf<")?;
<H as AlgorithmName>::write_alg_name(f)?;
f.write_str("> { ... }")
}
}
pub trait HmacImpl<H: OutputSizeUser>: sealed::Sealed<H> {}
impl<H: OutputSizeUser, T: sealed::Sealed<H>> HmacImpl<H> for T {}