#![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 hmac_drbg;
pub use bigint;
pub use hmac;
pub use hmac::digest::array::typenum::consts;
use crate::hmac_drbg::HmacDrbg;
use bigint::{Encoding, Limb, Unsigned};
use core::fmt;
use hmac::digest::{Digest, OutputSizeUser, block_api::BlockSizeUser};
pub struct KGenerator<'a, D: OutputSizeUser, U> {
drbg: HmacDrbg<D>,
q: &'a U,
}
impl<'a, D, U> KGenerator<'a, D, U>
where
D: BlockSizeUser + Digest,
U: Unsigned + Encoding,
{
pub fn new(x: &[u8], h: &[u8], data: &[u8], q: &'a U) -> Self {
let mut h_scratch = q.to_be_bytes();
let h_ref = &mut h_scratch.as_mut()[..x.len()];
bits2octets(h, q, h_ref);
let drbg = HmacDrbg::<D>::new(x, h_ref, data);
Self { drbg, q }
}
pub fn fill_next_k(&mut self, k: &mut [u8]) {
debug_assert_eq!(self.q.bits().div_ceil(8).try_into(), Ok(k.len()));
loop {
self.drbg.fill_bytes(k);
let candidate_k = bits2int(k, self.q);
if ((!candidate_k.is_zero()) & candidate_k.ct_lt(self.q)).to_bool() {
int2octets(&candidate_k, self.q, k);
return;
}
}
}
}
impl<'a, D, U> fmt::Debug for KGenerator<'a, D, U>
where
D: BlockSizeUser + Digest,
U: Unsigned + Encoding,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KGenerator").finish_non_exhaustive()
}
}
#[inline]
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
fn bits2int<U>(mut b: &[u8], q: &U) -> U
where
U: Unsigned + Encoding,
{
debug_assert!(!b.is_empty());
let qlen = q.bits();
let mut blen = u32::try_from(b.len())
.ok()
.and_then(|len| len.checked_mul(8))
.expect("overflow");
let bits_diff = blen.saturating_sub(qlen);
let bytes_to_discard = (bits_diff >> 3) as usize;
if bytes_to_discard > 0 {
b = &b[..b.len() - bytes_to_discard];
blen -= (bytes_to_discard as u32) * 8;
}
let mut int = U::from_be_slice_truncated(b, blen);
int.shr_assign(blen.saturating_sub(qlen));
int
}
#[allow(clippy::as_conversions)]
fn int2octets<'a, U: Unsigned>(x: &U, q: &U, out: &'a mut [u8]) -> &'a [u8] {
debug_assert!(x < q);
let qlen = q.bits();
let rlen = qlen.div_ceil(8) as usize;
debug_assert!(out.len() >= rlen);
let out = &mut out[..rlen];
let xlimbs: &[Limb] = x.as_ref();
for (limb, chunk) in xlimbs.iter().zip(out.rchunks_mut(Limb::BYTES)) {
let bytes = limb.to_be_bytes();
let offset = Limb::BYTES.saturating_sub(chunk.len());
chunk.copy_from_slice(&bytes[offset..]);
}
out
}
fn bits2octets<'a, U>(b: &[u8], q: &U, out: &'a mut [u8]) -> &'a [u8]
where
U: Unsigned + Encoding,
{
let z1 = bits2int(b, q);
let mut z2 = z1.wrapping_sub(q);
z2.ct_assign(&z1, z1.ct_lt(q));
int2octets(&z2, q, out)
}
#[cfg(test)]
mod tests {
mod bits2int {
use crate::bits2int;
use bigint::{BitOps, BoxedUint, U256};
#[test]
fn left_pads_when_blen_is_shorter_than_qlen() {
let q = U256::from_u64(0x800);
assert_eq!(bits2int(&[0x2a], &q), U256::from_u64(0x2a));
}
#[test]
fn keeps_exact_qlen_bits() {
let q = U256::from_u64(0x80);
assert_eq!(bits2int(&[0xab], &q), U256::from_u64(0xab));
}
#[test]
fn discards_trailing_bytes_then_shifts_remaining_bits() {
let q = U256::ONE.shl_vartime(162);
let b = [0xff; 32];
assert_eq!(
bits2int(&b, &q),
U256::from_be_hex(
"000000000000000000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
)
);
}
#[test]
fn truncates_leftmost_bits_on_byte_boundary() {
let q = U256::from_u64(0x80);
assert_eq!(bits2int(&[0xab, 0xcd], &q), U256::from_u64(0xab));
}
#[test]
fn truncates_leftmost_bits_across_partial_byte() {
let q = U256::from_u64(0x800);
assert_eq!(bits2int(&[0xab, 0xcd], &q), U256::from_u64(0xabc));
}
#[test]
fn boxed_matches_fixed_for_short_input() {
let q = U256::from_be_hex(
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
);
let q_boxed = BoxedUint::from_be_slice(&q.to_be_bytes(), q.bits_precision()).unwrap();
let h = [0xab; 20];
assert_eq!(bits2int(&h, &q_boxed), bits2int(&h, &q),);
}
}
mod int2octets {
use crate::int2octets;
use bigint::U256;
#[test]
fn encodes_single_octet() {
let q = U256::from_u64(0x80);
let x = U256::from_u64(0x2a);
let mut out = [0u8; 32];
assert_eq!(int2octets(&x, &q, &mut out), &[0x2a]);
}
#[test]
fn left_pads_to_qlen_octets() {
let q = U256::from_u64(0x800);
let x = U256::from_u64(0x2a);
let mut out = [0u8; 32];
assert_eq!(int2octets(&x, &q, &mut out), &[0x00, 0x2a]);
}
#[test]
fn int2octets_preserves_full_width_value() {
let q = U256::from_u64(0x800);
let x = U256::from_u64(0x7ff);
let mut out = [0u8; 32];
assert_eq!(int2octets(&x, &q, &mut out), &[0x07, 0xff]);
}
}
mod bits2octets {
use crate::bits2octets;
use bigint::{BoxedUint, U256};
const EXAMPLE_Q: U256 =
U256::from_be_hex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
#[test]
fn already_reduced() {
let q = U256::from_u64(0x80);
let mut out = [0u8; 32];
assert_eq!(bits2octets(&[0x2a], &q, &mut out), &[0x2a]);
}
#[test]
fn needs_reduction() {
let q = U256::from_u64(0x80);
let mut out = [0u8; 32];
assert_eq!(bits2octets(&[0xff], &q, &mut out), &[0x7f]);
}
#[test]
fn left_pads_when_blen_is_shorter_than_qlen() {
let h = [0xab; 20];
let mut out = [0u8; 32];
assert_eq!(
bits2octets(&h, &EXAMPLE_Q, &mut out),
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
],
);
}
#[test]
fn boxed_left_pads_short_hash_to_rlen() {
let boxed_q = BoxedUint::from_be_slice(&EXAMPLE_Q.to_be_bytes(), 256).unwrap();
let h = [0xab; 20];
let mut out = [0u8; 32];
assert_eq!(
bits2octets(&h, &boxed_q, &mut out),
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab,
],
);
}
#[test]
fn boxed_160_bit_hash_with_256_bit_q() {
let q = U256::from_be_hex(
"87A8E61DB4B6663CFFBBD19C6519599998CEEF608660DD0F25D2CE4EDDBF8A9B",
);
let boxed_q = BoxedUint::from_be_slice(&q.to_be_bytes(), 256).unwrap();
let h = [
0x81, 0x53, 0x2b, 0x5b, 0xe0, 0x21, 0xe9, 0xb6, 0xe9, 0x87, 0xf3, 0x37, 0x66, 0x31,
0x97, 0x4e, 0xa3, 0x1b, 0x72, 0x37,
];
let mut out = [0u8; 32];
let actual = bits2octets(&h, &boxed_q, &mut out);
assert_eq!(&actual[..12], &[0u8; 12]);
assert_eq!(&actual[12..], &h);
}
}
}