use core::cell::RefCell;
use rand_core::{RngCore, CryptoRng};
use merlin::Transcript;
use curve25519_dalek::digest::{Update, FixedOutput, ExtendableOutput, XofReader};
use curve25519_dalek::digest::generic_array::typenum::{U32, U64};
use curve25519_dalek::ristretto::CompressedRistretto; use curve25519_dalek::scalar::Scalar;
pub trait SigningTranscript {
fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]);
fn proto_name(&mut self, label: &'static [u8]) {
self.commit_bytes(b"proto-name", label);
}
fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) {
self.commit_bytes(label, compressed.as_bytes());
}
fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]);
fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar {
let mut buf = [0; 64];
self.challenge_bytes(label, &mut buf);
Scalar::from_bytes_mod_order_wide(&buf)
}
fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar {
let mut scalar_bytes = [0u8; 64];
self.witness_bytes(label, &mut scalar_bytes, nonce_seeds);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) {
self.witness_bytes_rng(label, dest, nonce_seeds, super::getrandom_or_panic())
}
fn witness_bytes_rng<R>(
&self,
label: &'static [u8],
dest: &mut [u8],
nonce_seeds: &[&[u8]],
rng: R,
) where
R: RngCore + CryptoRng;
}
#[rustfmt::skip]
impl<T> SigningTranscript for &mut T
where T: SigningTranscript + ?Sized,
{
#[inline(always)]
fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])
{ (**self).commit_bytes(label,bytes) }
#[inline(always)]
fn proto_name(&mut self, label: &'static [u8])
{ (**self).proto_name(label) }
#[inline(always)]
fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto)
{ (**self).commit_point(label, compressed) }
#[inline(always)]
fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])
{ (**self).challenge_bytes(label,dest) }
#[inline(always)]
fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar
{ (**self).challenge_scalar(label) }
#[inline(always)]
fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar
{ (**self).witness_scalar(label,nonce_seeds) }
#[inline(always)]
fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])
{ (**self).witness_bytes(label,dest,nonce_seeds) }
#[inline(always)]
fn witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R)
where R: RngCore+CryptoRng
{ (**self).witness_bytes_rng(label,dest,nonce_seeds,rng) }
}
impl SigningTranscript for Transcript {
fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
Transcript::append_message(self, label, bytes)
}
fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) {
Transcript::challenge_bytes(self, label, dest)
}
fn witness_bytes_rng<R>(
&self,
label: &'static [u8],
dest: &mut [u8],
nonce_seeds: &[&[u8]],
mut rng: R,
) where
R: RngCore + CryptoRng,
{
let mut br = self.build_rng();
for ns in nonce_seeds {
br = br.rekey_with_witness_bytes(label, ns);
}
let mut r = br.finalize(&mut rng);
r.fill_bytes(dest)
}
}
#[derive(Clone)] pub struct SigningContext(Transcript);
#[inline(always)]
pub fn signing_context(context: &[u8]) -> SigningContext {
SigningContext::new(context)
}
impl SigningContext {
#[inline(always)]
pub fn new(context: &[u8]) -> SigningContext {
let mut t = Transcript::new(b"SigningContext");
t.append_message(b"", context);
SigningContext(t)
}
#[inline(always)]
pub fn bytes(&self, bytes: &[u8]) -> Transcript {
let mut t = self.0.clone();
t.append_message(b"sign-bytes", bytes);
t
}
#[inline(always)]
pub fn xof<D: ExtendableOutput>(&self, h: D) -> Transcript {
let mut prehash = [0u8; 32];
h.finalize_xof().read(&mut prehash);
let mut t = self.0.clone();
t.append_message(b"sign-XoF", &prehash);
t
}
#[inline(always)]
pub fn hash256<D: FixedOutput<OutputSize = U32>>(&self, h: D) -> Transcript {
let mut prehash = [0u8; 32];
prehash.copy_from_slice(h.finalize_fixed().as_slice());
let mut t = self.0.clone();
t.append_message(b"sign-256", &prehash);
t
}
#[inline(always)]
pub fn hash512<D: FixedOutput<OutputSize = U64>>(&self, h: D) -> Transcript {
let mut prehash = [0u8; 64];
prehash.copy_from_slice(h.finalize_fixed().as_slice());
let mut t = self.0.clone();
t.append_message(b"sign-512", &prehash);
t
}
}
pub struct XoFTranscript<H>(H)
where
H: Update + ExtendableOutput + Clone;
fn input_bytes<H: Update>(h: &mut H, bytes: &[u8]) {
let l = bytes.len() as u64;
h.update(&l.to_le_bytes());
h.update(bytes);
}
impl<H> XoFTranscript<H>
where
H: Update + ExtendableOutput + Clone,
{
#[inline(always)]
pub fn new(h: H) -> XoFTranscript<H> {
XoFTranscript(h)
}
}
impl<H> From<H> for XoFTranscript<H>
where
H: Update + ExtendableOutput + Clone,
{
#[inline(always)]
fn from(h: H) -> XoFTranscript<H> {
XoFTranscript(h)
}
}
impl<H> SigningTranscript for XoFTranscript<H>
where
H: Update + ExtendableOutput + Clone,
{
fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
self.0.update(b"co");
input_bytes(&mut self.0, label);
input_bytes(&mut self.0, bytes);
}
fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) {
self.0.update(b"ch");
input_bytes(&mut self.0, label);
let l = dest.len() as u64;
self.0.update(&l.to_le_bytes());
self.0.clone().chain(b"xof").finalize_xof().read(dest);
}
fn witness_bytes_rng<R>(
&self,
label: &'static [u8],
dest: &mut [u8],
nonce_seeds: &[&[u8]],
mut rng: R,
) where
R: RngCore + CryptoRng,
{
let mut h = self.0.clone().chain(b"wb");
input_bytes(&mut h, label);
for ns in nonce_seeds {
input_bytes(&mut h, ns);
}
let l = dest.len() as u64;
h.update(&l.to_le_bytes());
let mut r = [0u8; 32];
rng.fill_bytes(&mut r);
h.update(&r);
h.finalize_xof().read(dest);
}
}
pub struct SigningTranscriptWithRng<T, R>
where
T: SigningTranscript,
R: RngCore + CryptoRng,
{
t: T,
rng: RefCell<R>,
}
#[rustfmt::skip]
impl<T,R> SigningTranscript for SigningTranscriptWithRng<T,R>
where T: SigningTranscript, R: RngCore+CryptoRng
{
fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])
{ self.t.commit_bytes(label, bytes) }
fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])
{ self.t.challenge_bytes(label, dest) }
fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])
{ self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut()) }
fn witness_bytes_rng<RR>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: RR)
where RR: RngCore+CryptoRng
{ self.t.witness_bytes_rng(label,dest,nonce_seeds,rng) }
}
pub fn attach_rng<T, R>(t: T, rng: R) -> SigningTranscriptWithRng<T, R>
where
T: SigningTranscript,
R: RngCore + CryptoRng,
{
SigningTranscriptWithRng { t, rng: RefCell::new(rng) }
}
#[cfg(feature = "rand_chacha")]
use rand_chacha::ChaChaRng;
#[cfg(feature = "rand_chacha")]
pub fn attach_chacharng<T>(t: T, seed: [u8; 32]) -> SigningTranscriptWithRng<T, ChaChaRng>
where
T: SigningTranscript,
{
use rand_core::SeedableRng;
attach_rng(t, ChaChaRng::from_seed(seed))
}