use crate::traits::UnsignedInt;
use aes::{
Aes128, Aes192, Aes256,
cipher::{KeyIvInit, StreamCipher},
};
use ctr::Ctr128BE;
use digest::{
Digest, ExtendableOutput, HashMarker, OutputSizeUser, Update, XofReader,
block_buffer::Eager,
core_api::{
BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore,
},
typenum::{IsLess, Le, NonZero, U256},
};
use hkdf::{self, Hkdf};
use hmac::{Hmac, Mac};
use sha3::Shake256;
use std::marker::PhantomData;
const D_1: u8 = 0x01;
const D_2: u8 = 0x02;
const D_3: u8 = 0x03;
const D_4: u8 = 0x04;
const D_5: u8 = 0x05;
const D_6: u8 = 0x06;
const D_7: u8 = 0x07;
pub struct Prf<D> {
_digest: PhantomData<D>,
}
impl<D> Prf<D>
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,
{
pub fn init_commits<T>(
arr: &[Vec<u8>],
key: &[u8],
encode: fn(T) -> Vec<u8>,
) -> Vec<Vec<u8>>
where
T: UnsignedInt,
{
let mut commits = Vec::with_capacity(arr.len());
for i in 0..arr.len() {
let mut mac = Hmac::<D>::new_from_slice(&key)
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_1]);
Mac::update(&mut mac, &encode(T::from_usize(i)));
Mac::update(&mut mac, &encode(T::from_usize(arr[i].len())));
Mac::update(&mut mac, &arr[i]);
commits.push(mac.finalize().into_bytes().to_vec());
}
commits
}
pub fn mix<T>(
arr: &[Vec<u8>],
prk: &[u8],
rounds: usize,
encode: fn(T) -> Vec<u8>,
) -> Vec<Vec<u8>>
where
T: UnsignedInt,
{
let mut mixed = arr.to_vec();
let key_len = <D as OutputSizeUser>::output_size();
for i in 0..rounds {
let info = format!("ROUND{}", i);
let info_bytes = info.as_bytes();
let hk = hkdf::Hkdf::<D>::from_prk(prk)
.expect("PRK should be large enough");
let mut key = vec![0u8; key_len];
hk.expand(&info_bytes, &mut key)
.expect("okm length should match the hash digest length");
let mut tweak_hasher = D::new();
tweak_hasher.update(&key);
tweak_hasher.update(&encode(T::from_usize(i)));
let tweak = tweak_hasher.finalize().to_vec();
let mut sponge = Shake256::default();
sponge.update(&tweak);
for j in 0..mixed.len() {
sponge.update(&mixed[j]);
sponge.update(&encode(T::from_usize(j)));
}
let mut sponge_reader = sponge.finalize_xof();
let mut tmp = Vec::with_capacity(mixed.len());
for j in 0..mixed.len() {
let mut element = vec![0u8; mixed[j].len()];
sponge_reader.read(&mut element);
tmp.push(element);
}
mixed = tmp;
}
mixed
}
pub fn next<T>(
arr: &[Vec<u8>],
context: &str,
prk: &[u8],
subset: usize,
counter: T,
encode: fn(T) -> Vec<u8>,
decode: fn(&[u8]) -> T,
dst: &mut [u8],
) where
T: UnsignedInt,
{
let commit = Self::commitment::<T>(arr, encode);
let key_len = <D as OutputSizeUser>::output_size();
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!("{}-SUBKEYS", context);
hk.expand(&info.as_bytes(), &mut key_1)
.expect("okm length should match the hash digest length");
let mut key_2 = vec![0u8; key_len];
info = format!("{}-INDICES", context);
hk.expand(&info.as_bytes(), &mut key_2)
.expect("okm length should match the hash digest length");
let mut key_3 = vec![0u8; key_len];
info = format!("{}-PRF", context);
hk.expand(&info.as_bytes(), &mut key_3)
.expect("okm length should match the hash digest length");
let k_s = Self::subkeys::<T>(arr, &key_1, &commit, encode);
let k_i = Self::indices::<T>(
&key_2,
&commit,
T::from_usize(arr.len()),
T::from_usize(subset),
counter,
encode,
decode,
);
let acc = Self::combine::<T>(&k_s, &k_i, &commit, counter, encode);
let (prf_key, nonce) =
Self::derive_key_nonce::<T>(&key_3, &commit, counter, &acc, encode);
dst.fill(0);
match prf_key.len() {
16 => {
let mut aes_key = [0u8; 16];
aes_key.copy_from_slice(&prf_key);
let mut cipher =
Ctr128BE::<Aes128>::new(&aes_key.into(), &nonce.into());
cipher.apply_keystream(dst);
}
24 => {
let mut aes_key = [0u8; 24];
aes_key.copy_from_slice(&prf_key);
let mut cipher =
Ctr128BE::<Aes192>::new(&aes_key.into(), &nonce.into());
cipher.apply_keystream(dst);
}
32 => {
let mut aes_key = [0u8; 32];
aes_key.copy_from_slice(&prf_key);
let mut cipher =
Ctr128BE::<Aes256>::new(&aes_key.into(), &nonce.into());
cipher.apply_keystream(dst);
}
_ => panic!("key length {} is invalid for AES-CTR", prf_key.len()),
}
}
fn commitment<T>(arr: &[Vec<u8>], encode: fn(T) -> Vec<u8>) -> Vec<u8>
where
T: UnsignedInt,
{
let mut hasher = D::new();
hasher.update(&[D_2]);
hasher.update(&encode(T::from_usize(arr.len())));
for i in 0..arr.len() {
hasher.update(&encode(T::from_usize(i)));
hasher.update(&encode(T::from_usize(arr[i].len())));
hasher.update(&arr[i]);
}
hasher.finalize().to_vec()
}
fn subkeys<T>(
arr: &[Vec<u8>],
key: &[u8],
commit: &[u8],
encode: fn(T) -> Vec<u8>,
) -> Vec<Vec<u8>>
where
T: UnsignedInt,
{
let mut k_s = Vec::with_capacity(arr.len());
for i in 0..arr.len() {
let mut mac = Hmac::<D>::new_from_slice(&key)
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_3]);
Mac::update(&mut mac, &encode(T::from_usize(i)));
Mac::update(&mut mac, &encode(T::from_usize(arr[i].len())));
Mac::update(&mut mac, &arr[i]);
Mac::update(&mut mac, commit);
let subkey: Vec<u8> = mac.finalize().into_bytes().to_vec();
k_s.push(subkey);
}
k_s
}
fn indices<T>(
key: &[u8],
commit: &[u8],
n: T,
s: T,
counter: T,
encode: fn(T) -> Vec<u8>,
decode: fn(&[u8]) -> T,
) -> Vec<T>
where
T: UnsignedInt,
{
let n_usize = n.as_usize();
let s_usize = s.as_usize();
let mut k_i: Vec<T> = (0..n_usize).map(|i| T::from_usize(i)).collect();
let ctr_bytes_ext = encode(counter);
let mut ctr: T = T::from(0);
let mut next = || {
let ctr_bytes_in = encode(ctr);
ctr = ctr.wrapping_add(T::from(1));
let mut mac = Hmac::<D>::new_from_slice(key)
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_4]);
Mac::update(&mut mac, commit);
Mac::update(&mut mac, &ctr_bytes_ext);
Mac::update(&mut mac, &ctr_bytes_in);
let bytes: Vec<u8> = mac.finalize().into_bytes().to_vec();
bytes
};
let mut p: Vec<u8> = Vec::new();
for i in 0..s_usize {
let i_c = T::from_usize(i);
let range = n.wrapping_sub(i_c);
let rem = T::from(0).wrapping_sub(range) % range;
let mut v: T;
if rem == T::from(0) {
if p.len() < T::SIZE {
p = next();
}
v = decode(&p[0..T::SIZE]);
p.drain(0..T::SIZE);
} else {
let limit = T::from(0).wrapping_sub(rem);
loop {
if p.len() < T::SIZE {
p = next();
}
v = decode(&p[0..T::SIZE]);
p.drain(0..T::SIZE);
if v.as_usize() < limit.as_usize() {
break;
}
}
}
let j_usize = i + (v % range).as_usize();
k_i.swap(i, j_usize);
}
k_i[0..s_usize].to_vec()
}
fn combine<T>(
subkeys: &Vec<Vec<u8>>,
indices: &Vec<T>,
commit: &[u8],
counter: T,
encode: fn(T) -> Vec<u8>,
) -> Vec<u8>
where
T: UnsignedInt,
{
let output_len = <D as OutputSizeUser>::output_size();
let ctr_bytes = encode(counter);
let mut acc = vec![0u8; output_len];
for i in indices.iter() {
let mut mac = Hmac::<D>::new_from_slice(&subkeys[i.as_usize()])
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_5]);
Mac::update(&mut mac, commit);
Mac::update(&mut mac, &ctr_bytes);
let y = mac.finalize().into_bytes().to_vec();
for j in 0..output_len {
acc[j] ^= y[j]
}
}
acc
}
fn derive_key_nonce<T>(
key: &[u8],
commit: &[u8],
counter: T,
acc: &[u8],
encode: fn(T) -> Vec<u8>,
) -> (Vec<u8>, [u8; 16])
where
T: UnsignedInt,
{
let digest_len = <D as OutputSizeUser>::output_size();
let key_len = if digest_len >= 32 {
32 } else if digest_len >= 24 {
24 } else {
16 };
let ctr_bytes = encode(counter);
let mut mac = Hmac::<D>::new_from_slice(key)
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_6]);
Mac::update(&mut mac, commit);
Mac::update(&mut mac, &ctr_bytes);
Mac::update(&mut mac, acc);
let key_full: Vec<u8> = mac.finalize().into_bytes().to_vec();
let mut prf_key = vec![0u8; key_len];
if key_full.len() >= key_len {
prf_key.copy_from_slice(&key_full[0..key_len]);
} else {
prf_key[0..key_full.len()].copy_from_slice(&key_full);
let hk = Hkdf::<D>::new(None, &key_full);
hk.expand(b"AES_KEY_EXPANSION", &mut prf_key[key_full.len()..])
.expect("HKDF expansion should succeed");
}
let mut mac = Hmac::<D>::new_from_slice(key)
.expect("HMAC can take key of any size");
Mac::update(&mut mac, &[D_7]);
Mac::update(&mut mac, commit);
Mac::update(&mut mac, &ctr_bytes);
let nonce_full: Vec<u8> = mac.finalize().into_bytes().to_vec();
let mut nonce = [0u8; 16];
nonce.copy_from_slice(&nonce_full[0..16]);
(prf_key, nonce)
}
}