use aead::{Aead, KeyInit};
use rand::seq::SliceRandom;
use rand_core::CryptoRngCore;
use crate::{
Decoy, Hint, HintSeed, Hinting,
curves::{KeyPair, sealed},
error::*,
};
pub struct Hints<H, const S: usize> {
inner: Vec<H>,
}
impl<K: KeyPair, A: Aead + KeyInit, const L: usize, const S: usize> Hints<Hint<K, A, L>, S>
where
Hint<K, A, L>: Hinting<K, L>,
{
pub fn new(
hint_seeds: &[HintSeed<K, L>],
salt: &[u8],
csprng: &mut impl CryptoRngCore,
) -> Result<Self>
where
HintSeed<K, L>: Decoy,
K::SecretKey: sealed::RandomSecretKey,
{
let hint_seeds_len = hint_seeds.len();
if hint_seeds_len > S {
return Err(Error::HintsLength);
}
let hints_secret = <K::SecretKey as sealed::RandomSecretKey>::random_secret_key(csprng);
let decoys: Vec<_> = (0..S)
.map(|_| HintSeed::<K, L>::random_decoy(csprng))
.collect();
let mut indices: Vec<usize> = (0..S).collect();
indices.shuffle(csprng);
let inner: Vec<_> = indices
.into_iter()
.map(|i| {
let hint_seed = if i < hint_seeds_len {
&hint_seeds[i]
} else {
&decoys[i - hint_seeds_len]
};
Hint::<K, A, L>::from_blinding_factor_secret(
&hints_secret,
&hint_seed.blinded_public_key,
&hint_seed.message,
salt,
)
})
.collect::<Result<_>>()?;
Ok(Self { inner })
}
pub fn as_slice(&self) -> &[Hint<K, A, L>] {
self.inner.as_slice()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
let hint_bytes_length = Hint::<K, A, L>::bytes_length();
if bytes.len() / hint_bytes_length != S {
return Err(Error::HintsLength);
}
Ok(Self {
inner: bytes
.chunks_exact(hint_bytes_length)
.map(|c| Hint::<K, A, L>::from_bytes(c))
.collect::<Result<Vec<_>>>()?,
})
}
pub fn to_bytes(self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(Hint::<K, A, L>::bytes_length() * S);
for hint in self.inner {
bytes.append(&mut hint.to_bytes());
}
bytes
}
}