mod errors;
mod prf;
mod traits;
use digest::{
Digest, HashMarker, OutputSizeUser,
block_buffer::Eager,
core_api::{
BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore,
},
typenum::{IsLess, Le, NonZero, U256},
};
pub use errors::DrbgError;
use hkdf::Hkdf;
use prf::Prf;
use rand_core::RngCore;
use std::marker::PhantomData;
pub use traits::UnsignedInt;
use zeroize::Zeroize;
#[derive(Copy, Clone)]
pub enum Endian {
LittleEndian,
BigEndian,
}
pub struct Drbg<D, T> {
arr: Vec<Vec<u8>>,
prk: Vec<u8>,
context: String,
ctr: T,
endian: Endian,
_digest: PhantomData<D>,
}
impl<D, T> Drbg<D, T>
where
D: Digest + CoreProxy + OutputSizeUser,
D::Core: Sync
+ HashMarker
+ UpdateCore
+ FixedOutputCore
+ BufferKindUser<BufferKind = Eager>
+ Default
+ Clone
+ BlockSizeUser,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
T: UnsignedInt,
{
pub fn new_le(
arr: &Vec<Vec<u8>>,
context: Option<&str>,
init: bool,
) -> Result<Self, DrbgError> {
Self::validate_array(arr)?;
Self::validate_digest()?;
let endian = Endian::LittleEndian;
if init {
let arr_concat: Vec<u8> = arr.iter().flatten().copied().collect();
let mut hasher = D::new();
hasher.update(&arr_concat);
let nonce = hasher.finalize().to_vec();
let arr_init = Self::initialize(&arr, context, nonce, 1, endian);
Ok(Self::new_from(&arr_init, context, endian))
} else {
Ok(Self::new_from(&arr, context, endian))
}
}
pub fn new_be(
arr: &Vec<Vec<u8>>,
context: Option<&str>,
init: bool,
) -> Result<Self, DrbgError> {
Self::validate_array(arr)?;
Self::validate_digest()?;
let endian = Endian::BigEndian;
if init {
let arr_concat: Vec<u8> = arr.iter().flatten().copied().collect();
let mut hasher = D::new();
hasher.update(&arr_concat);
let nonce = hasher.finalize().to_vec();
let arr_init = Self::initialize(&arr, context, nonce, 1, endian);
Ok(Self::new_from(&arr_init, context, endian))
} else {
Ok(Self::new_from(&arr, context, endian))
}
}
pub fn initialize(
arr: &[Vec<u8>],
context: Option<&str>,
nonce: Vec<u8>,
rounds: usize,
endian: Endian,
) -> Vec<Vec<u8>> {
let key_len = <D as OutputSizeUser>::output_size();
let arr_concat: Vec<u8> = arr.iter().flatten().copied().collect();
let prk = Self::derive_prk(&arr_concat, &nonce);
let hk = Hkdf::<D>::from_prk(&prk).expect("PRK should be large enough");
let mut key_1 = vec![0u8; key_len];
let mut info = format!("{}-COMMIT", context.unwrap_or(""));
hk.expand(&info.as_bytes().to_vec(), &mut key_1)
.expect("okm length should match the hash digest length");
let mut key_2 = vec![0u8; key_len];
info = format!("{}-MIX", context.unwrap_or(""));
hk.expand(&info.as_bytes().to_vec(), &mut key_2)
.expect("okm length should match the hash digest length");
let committed: Vec<Vec<u8>>;
match endian {
Endian::LittleEndian => {
committed =
Prf::<D>::init_commits(&arr, &key_1, T::to_le_bytes);
}
Endian::BigEndian => {
committed =
Prf::<D>::init_commits(&arr, &key_1, T::to_be_bytes);
}
}
let mixed: Vec<Vec<u8>>;
match endian {
Endian::LittleEndian => {
mixed =
Prf::<D>::mix(&committed, &key_2, rounds, T::to_le_bytes);
}
Endian::BigEndian => {
mixed =
Prf::<D>::mix(&committed, &key_2, rounds, T::to_be_bytes);
}
}
mixed
}
pub fn next_u32_subset(&mut self, subset: usize) -> u32 {
let mut bytes = [0u8; 4];
self.fill_bytes_subset(subset, &mut bytes);
match self.endian {
Endian::LittleEndian => u32::from_le_bytes(bytes),
Endian::BigEndian => u32::from_be_bytes(bytes),
}
}
pub fn next_u64_subset(&mut self, subset: usize) -> u64 {
let mut bytes = [0u8; 8];
self.fill_bytes_subset(subset, &mut bytes);
match self.endian {
Endian::LittleEndian => u64::from_le_bytes(bytes),
Endian::BigEndian => u64::from_be_bytes(bytes),
}
}
pub fn fill_bytes_subset(&mut self, subset: usize, dst: &mut [u8]) {
let subset = subset.min(self.arr.len());
match T::SIZE {
4 => {
if self.ctr == T::MAX {
panic!("Counter exhausted u32 range")
}
}
8 => {
if self.ctr == T::MAX {
panic!("Counter exhausted u64 range")
}
}
_ => unreachable!("Only u32 and u64 supported"),
}
match &mut self.endian {
Endian::LittleEndian => Prf::<D>::next(
&self.arr,
&self.context,
&self.prk,
subset,
self.ctr,
T::to_le_bytes,
T::from_le_bytes,
dst,
),
Endian::BigEndian => Prf::<D>::next(
&self.arr,
&self.context,
&self.prk,
subset,
self.ctr,
T::to_be_bytes,
T::from_be_bytes,
dst,
),
}
self.ctr = self.ctr.wrapping_add(T::from(1));
let label = format!("{}-UPDATE", &self.context);
let label_bytes = &label.as_bytes().to_vec();
let mut tmp_prk = Self::derive_prk(&dst.to_vec(), &label_bytes);
let tmp_arr = match self.endian {
Endian::LittleEndian => {
Prf::<D>::mix(&self.arr, &tmp_prk, 1, T::to_le_bytes)
}
Endian::BigEndian => {
Prf::<D>::mix(&self.arr, &tmp_prk, 1, T::to_be_bytes)
}
};
let arr_concat: Vec<u8> = tmp_arr.iter().flatten().copied().collect();
let label = format!("{}-NEXT", &self.context);
let label_bytes = &label.as_bytes().to_vec();
tmp_prk = Self::derive_prk(&arr_concat, &label_bytes);
self.arr = tmp_arr;
self.prk = tmp_prk;
}
fn validate_array(arr: &Vec<Vec<u8>>) -> Result<(), DrbgError> {
if arr.is_empty() {
return Err(DrbgError::EmptyArray);
}
let empty_elements: Vec<usize> = arr
.iter()
.enumerate()
.filter_map(
|(i, element)| if element.is_empty() { Some(i) } else { None },
)
.collect();
if !empty_elements.is_empty() {
return Err(DrbgError::EmptyElement(empty_elements));
}
Ok(())
}
fn validate_digest() -> Result<(), DrbgError> {
let digest_len = <D as OutputSizeUser>::output_size();
if digest_len < 16 {
return Err(DrbgError::DigestTooSmall(digest_len));
}
Ok(())
}
fn derive_prk(ikm: &Vec<u8>, salt: &Vec<u8>) -> Vec<u8> {
let prk_len = <D as OutputSizeUser>::output_size();
let (prk_arr, _) = Hkdf::<D>::extract(Some(&salt), &ikm);
let mut prk = vec![0u8; prk_len];
prk.copy_from_slice(&prk_arr);
prk
}
fn new_from(
arr: &Vec<Vec<u8>>,
context: Option<&str>,
endian: Endian,
) -> Self {
let arr_concat: Vec<u8> = arr.iter().flatten().copied().collect();
let label = format!("{}-OUTPUT", context.unwrap_or(""));
let label_bytes = &label.as_bytes().to_vec();
let prk = Self::derive_prk(&arr_concat, &label_bytes);
Self {
arr: arr.to_vec(),
prk: prk,
context: context.unwrap_or("").to_string(),
ctr: T::from(0),
endian: endian,
_digest: PhantomData,
}
}
}
impl<D, T> RngCore for Drbg<D, T>
where
D: Digest + CoreProxy + OutputSizeUser,
D::Core: Sync
+ HashMarker
+ UpdateCore
+ FixedOutputCore
+ BufferKindUser<BufferKind = Eager>
+ Default
+ Clone
+ BlockSizeUser,
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
T: UnsignedInt,
{
fn next_u32(&mut self) -> u32 {
return self.next_u32_subset(self.arr.len());
}
fn next_u64(&mut self) -> u64 {
return self.next_u64_subset(self.arr.len());
}
fn fill_bytes(&mut self, dst: &mut [u8]) {
self.fill_bytes_subset(self.arr.len(), dst);
}
}
impl<D, T> Drop for Drbg<D, T> {
fn drop(&mut self) {
self.prk.zeroize();
for element in &mut self.arr {
element.zeroize();
}
}
}