use crate::{Scalar, hash::*, marker::*};
use core::marker::PhantomData;
use rand_core::RngCore;
pub trait NonceRng {
fn fill_bytes(&self, bytes: &mut [u8]);
}
impl<R: RngCore + Default> NonceRng for GlobalRng<R> {
fn fill_bytes(&self, bytes: &mut [u8]) {
R::default().fill_bytes(bytes);
}
}
impl<R: RngCore> NonceRng for core::cell::RefCell<R> {
fn fill_bytes(&self, bytes: &mut [u8]) {
self.borrow_mut().fill_bytes(bytes)
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<R: RngCore> NonceRng for std::sync::Mutex<R> {
fn fill_bytes(&self, bytes: &mut [u8]) {
self.lock().unwrap().fill_bytes(bytes)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Synthetic<H, R> {
rng: R,
nonce_hash: H,
aux_hash: H,
}
impl<H: Default, R: NonceRng> Synthetic<H, R> {
pub fn new(rng: R) -> Self {
Self {
rng,
nonce_hash: H::default(),
aux_hash: H::default(),
}
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct GlobalRng<R> {
inner: PhantomData<fn(R)>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Deterministic<H> {
nonce_hash: H,
}
pub trait NonceGen {
type Hash: Hash32;
fn begin_derivation(&self, secret: &Scalar<Secret, impl ZeroChoice>) -> Self::Hash;
}
impl<H: Hash32> NonceGen for Deterministic<H> {
type Hash = H;
fn begin_derivation(&self, secret: &Scalar<Secret, impl ZeroChoice>) -> Self::Hash {
self.nonce_hash.clone().add(secret)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
pub struct NoNonces;
impl<H: Tag> Tag for Deterministic<H> {
fn tag_vectored<'a>(self, tag: impl Iterator<Item = &'a [u8]> + Clone) -> Self {
Self {
nonce_hash: self
.nonce_hash
.tag_vectored(tag.chain(core::iter::once(b"/nonce".as_slice()))),
}
}
}
impl Tag for NoNonces {
fn tag_vectored<'a>(self, _tag: impl IntoIterator<Item = &'a [u8]>) -> Self {
self
}
}
impl<H, R> NonceGen for Synthetic<H, R>
where
H: Hash32,
R: NonceRng,
{
type Hash = H;
fn begin_derivation(&self, secret: &Scalar<Secret, impl ZeroChoice>) -> Self::Hash {
let sec_bytes = secret.to_bytes();
let mut aux_bytes = [0u8; 32];
self.rng.fill_bytes(&mut aux_bytes[..]);
let mut aux_hash = self.aux_hash.clone();
digest::Update::update(&mut aux_hash, &aux_bytes);
let mut bytes = [0u8; 32];
bytes.copy_from_slice(aux_hash.finalize_fixed().as_ref());
for (i, byte) in bytes.iter_mut().enumerate() {
*byte ^= sec_bytes[i]
}
self.nonce_hash.clone().add(&bytes[..])
}
}
impl<H: Tag, R> Tag for Synthetic<H, R> {
fn tag_vectored<'a>(self, tag: impl Iterator<Item = &'a [u8]> + Clone) -> Self {
Self {
nonce_hash: self
.nonce_hash
.tag_vectored(tag.clone().chain(core::iter::once(b"/nonce".as_slice()))),
aux_hash: self
.aux_hash
.tag_vectored(tag.chain(core::iter::once(b"/aux".as_slice()))),
rng: self.rng,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{derive_nonce, marker::Secret, s};
use rand::rngs::ThreadRng;
use sha2::Sha256;
macro_rules! get_nonce {
($nonce_gen:expr, $scalar:expr) => {
derive_nonce!(
nonce_gen => $nonce_gen,
secret => $scalar,
public => [b"test".as_ref()]
)
}
}
#[test]
fn deterministic_tests() {
use core::str::FromStr;
let nonce_gen_1 = Deterministic::<Sha256>::default().tag(b"PROTO_ONE");
let nonce_gen_2 = Deterministic::<Sha256>::default().tag(b"PROTO_TWO");
let one = s!(1);
let two = s!(2);
assert_eq!(get_nonce!(nonce_gen_1, one), get_nonce!(nonce_gen_1, one));
assert_ne!(get_nonce!(nonce_gen_1, one), get_nonce!(nonce_gen_1, two));
assert_ne!(get_nonce!(nonce_gen_1, one), get_nonce!(nonce_gen_2, one));
let app_nonce_gen_1 = nonce_gen_1.clone().tag(b"MY_APP");
let app_nonce_gen_2 = nonce_gen_2.tag(b"MY_APP");
assert_ne!(
get_nonce!(nonce_gen_1, one),
get_nonce!(app_nonce_gen_1, one)
);
assert_ne!(
get_nonce!(app_nonce_gen_1, one),
get_nonce!(app_nonce_gen_2, one)
);
assert_eq!(
get_nonce!(nonce_gen_1, one),
Scalar::<Secret>::from_str(
"34f7ce653cfa8454b3463726a599ef2925736442d2d06455974d6feae9450d90"
)
.unwrap()
)
}
#[test]
fn synthetic_nonce_gen_is_random() {
let nonce_gen_1 = Synthetic::<Sha256, GlobalRng<ThreadRng>>::default().tag(b"PROTO_ONE");
let one = s!(1);
assert_ne!(get_nonce!(nonce_gen_1, one), get_nonce!(nonce_gen_1, one));
}
#[test]
fn derive_nonce_macros_work_with_fixed_length_data() {
let _ = crate::derive_nonce_rng! {
nonce_gen => Deterministic::<Sha256>::default(),
secret => Scalar::random(&mut rand::thread_rng()),
public => [b"a fixed length array"],
seedable_rng => rand::rngs::StdRng,
};
let _ = crate::derive_nonce! {
nonce_gen => Deterministic::<Sha256>::default(),
secret => Scalar::random(&mut rand::thread_rng()),
public => [b"a fixed length array"],
};
}
}