#![no_std]
#![doc = include_str!("../README.md")]
#![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;
pub use hmac;
pub use hmac::digest::array::typenum::consts;
use core::fmt::{self, Debug};
use hmac::{
HmacReset,
digest::{
KeyInit, Mac, OutputSizeUser,
array::{Array, ArraySize},
block_api::EagerHash,
},
};
#[inline]
pub fn generate_k<D, N>(
x: &Array<u8, N>,
q: &Array<u8, N>,
h: &Array<u8, N>,
data: &[u8],
) -> Array<u8, N>
where
D: EagerHash,
N: ArraySize,
{
let mut k = Array::default();
generate_k_mut::<D>(x, q, h, data, &mut k);
k
}
#[inline]
pub fn generate_k_mut<D>(x: &[u8], q: &[u8], h: &[u8], data: &[u8], k: &mut [u8])
where
D: EagerHash,
{
let k_len = k.len();
assert_eq!(k_len, x.len());
assert_eq!(k_len, q.len());
assert_eq!(k_len, h.len());
debug_assert!(bool::from(ct::lt(h, q)));
let q_leading_zeros = ct::leading_zeros(q);
let q_has_leading_zeros = q_leading_zeros != 0;
let mut hmac_drbg = HmacDrbg::<D>::new(x, h, data);
loop {
hmac_drbg.fill_bytes(k);
if q_has_leading_zeros {
ct::rshift(k, q_leading_zeros);
}
if (!ct::is_zero(k) & ct::lt(k, q)).into() {
return;
}
}
}
pub struct HmacDrbg<D>
where
D: EagerHash,
{
k: HmacReset<D>,
v: Array<u8, <D::Core as OutputSizeUser>::OutputSize>,
}
impl<D> HmacDrbg<D>
where
D: EagerHash,
{
#[must_use]
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
pub fn new(entropy_input: &[u8], nonce: &[u8], personalization_string: &[u8]) -> Self {
let mut k = HmacReset::new(&Default::default());
let mut v = Array::default();
v.fill(0x01);
for i in 0..=1 {
k.update(&v);
k.update(&[i]);
k.update(entropy_input);
k.update(nonce);
k.update(personalization_string);
k = HmacReset::new_from_slice(&k.finalize().into_bytes()).expect("should work");
k.update(&v);
v = k.finalize_reset().into_bytes();
}
Self { k, v }
}
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
pub fn fill_bytes(&mut self, out: &mut [u8]) {
let mut out_chunks = out.chunks_exact_mut(self.v.len());
for out_chunk in &mut out_chunks {
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
}
let out_remainder = out_chunks.into_remainder();
if !out_remainder.is_empty() {
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
out_remainder.copy_from_slice(&self.v[..out_remainder.len()]);
}
self.k.update(&self.v);
self.k.update(&[0x00]);
self.k =
HmacReset::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("should work");
self.k.update(&self.v);
self.v = self.k.finalize_reset().into_bytes();
}
}
impl<D> Debug for HmacDrbg<D>
where
D: EagerHash,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("HmacDrbg").finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use crate::{
Array,
consts::{U21, U66},
generate_k,
};
use hex_literal::hex;
use sha2::{Digest, Sha256, Sha512};
#[test]
fn k163_sha256() {
let q = hex!("04000000000000000000020108A2E0CC0D99F8A5EF");
let x = hex!("009A4D6792295A7F730FC3F2B49CBC0F62E862272F");
let h2 = hex!("01795EDF0D54DB760F156D0DAC04C0322B3A204224");
let aad = b"";
let k = generate_k::<Sha256, U21>(&x.into(), &q.into(), &h2.into(), aad);
assert_eq!(k, hex!("023AF4074C90A02B3FE61D286D5C87F425E6BDD81B"));
}
#[test]
fn p521_sha512() {
let q = hex!(
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"
);
let x = hex!(
"00FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538"
);
let message = "sample";
let mut h = Array::<u8, U66>::default();
h[2..].copy_from_slice(&Sha512::digest(message));
let aad = b"";
let k = generate_k::<Sha512, U66>(&x.into(), &q.into(), &h, aad);
let expected_k = hex!(
"01DAE2EA071F8110DC26882D4D5EAE0621A3256FC8847FB9022E2B7D28E6F10198B1574FDD03A9053C08A1854A168AA5A57470EC97DD5CE090124EF52A2F7ECBFFD3"
);
assert_eq!(k, expected_k);
}
}