#![no_std]
#![doc = include_str!("../README.md")]
#![forbid(unsafe_code, clippy::unwrap_used)]
#![warn(missing_docs, rust_2018_idioms)]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
mod ct_cmp;
pub use hmac::digest::generic_array::typenum::consts;
use hmac::{
digest::{
core_api::BlockSizeUser,
generic_array::{ArrayLength, GenericArray},
Digest, FixedOutput, FixedOutputReset, Mac,
},
SimpleHmac,
};
pub type ByteArray<Size> = GenericArray<u8, Size>;
#[inline]
pub fn generate_k<D, N>(
x: &ByteArray<N>,
n: &ByteArray<N>,
h: &ByteArray<N>,
data: &[u8],
) -> ByteArray<N>
where
D: Digest + BlockSizeUser + FixedOutput<OutputSize = N> + FixedOutputReset,
N: ArrayLength<u8>,
{
let mut hmac_drbg = HmacDrbg::<D>::new(x, h, data);
loop {
let mut k = ByteArray::<N>::default();
hmac_drbg.fill_bytes(&mut k);
let k_is_zero = ct_cmp::ct_eq(&k, &ByteArray::default());
if (!k_is_zero & ct_cmp::ct_lt(&k, n)).into() {
return k;
}
}
}
pub struct HmacDrbg<D>
where
D: Digest + BlockSizeUser + FixedOutputReset,
{
k: SimpleHmac<D>,
v: GenericArray<u8, D::OutputSize>,
}
impl<D> HmacDrbg<D>
where
D: Digest + BlockSizeUser + FixedOutputReset,
{
pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self {
let mut k = SimpleHmac::new(&Default::default());
let mut v = GenericArray::default();
for b in &mut v {
*b = 0x01;
}
for i in 0..=1 {
k.update(&v);
k.update(&[i]);
k.update(entropy_input);
k.update(nonce);
k.update(additional_data);
k = SimpleHmac::new_from_slice(&k.finalize().into_bytes()).expect("HMAC error");
k.update(&v);
v = k.finalize_reset().into_bytes();
}
Self { k, v }
}
pub fn fill_bytes(&mut self, out: &mut [u8]) {
for out_chunk in out.chunks_mut(self.v.len()) {
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
}
self.k.update(&self.v);
self.k.update(&[0x00]);
self.k =
SimpleHmac::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("HMAC error");
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
}
}