mirror of
https://github.com/saymrwulf/pasta_curves-source.git
synced 2026-09-04 20:03:39 +00:00
Change API for dealing with transcripts to integrate proof reading/writing.
This commit is contained in:
parent
fb37172ffa
commit
fb232ddec0
1 changed files with 208 additions and 115 deletions
|
|
@ -2,125 +2,221 @@
|
||||||
//! transcripts.
|
//! transcripts.
|
||||||
|
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::arithmetic::{CurveAffine, FieldExt};
|
use crate::arithmetic::{CurveAffine, FieldExt};
|
||||||
|
|
||||||
/// This is a generic interface for a sponge function that can be used for
|
use std::io::{self, Read, Write};
|
||||||
/// Fiat-Shamir transformations.
|
use std::marker::PhantomData;
|
||||||
pub trait Hasher<F: FieldExt>: Clone + Send + Sync + 'static {
|
|
||||||
/// Initialize the sponge with some key.
|
/// Generic transcript view (from either the prover or verifier's perspective)
|
||||||
fn init(key: F) -> Self;
|
pub trait Transcript<C: CurveAffine> {
|
||||||
/// Absorb a field element into the sponge.
|
/// Squeeze a challenge (in the base field) from the transcript.
|
||||||
fn absorb(&mut self, value: F);
|
fn squeeze_challenge(&mut self) -> C::Base;
|
||||||
/// Square a field element out of the sponge.
|
|
||||||
fn squeeze(&mut self) -> F;
|
/// Writing the point to the transcript without writing it to the proof,
|
||||||
|
/// treating it as a common input.
|
||||||
|
fn common_point(&mut self, point: C) -> io::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is just a simple (and completely broken) hash function, standing in for
|
/// Transcript view from the perspective of a verifier that has access to an
|
||||||
/// some algebraic hash function that we'll switch to later.
|
/// input stream of data from the prover to the verifier.
|
||||||
|
pub trait TranscriptRead<R: Read, C: CurveAffine>: Transcript<C> {
|
||||||
|
/// Initialize the transcript with a key and an input stream.
|
||||||
|
fn init(reader: R, key: C::Base) -> Self;
|
||||||
|
|
||||||
|
/// Read a curve point from the prover.
|
||||||
|
fn read_point(&mut self) -> io::Result<C>;
|
||||||
|
|
||||||
|
/// Read a curve scalar from the prover.
|
||||||
|
fn read_scalar(&mut self) -> io::Result<C::Scalar>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transcript view from the perspective of a prover that has access to an
|
||||||
|
/// output stream of messages from the prover to the verifier.
|
||||||
|
pub trait TranscriptWrite<W: Write, C: CurveAffine>: Transcript<C> {
|
||||||
|
/// Forked transcript that does not write to the proof structure.
|
||||||
|
type ForkedTranscript: TranscriptWrite<io::Sink, C>;
|
||||||
|
|
||||||
|
/// Initialize the transcript with a key and an output stream.
|
||||||
|
fn init(writer: W, key: C::Base) -> Self;
|
||||||
|
|
||||||
|
/// Write a curve point to the proof and the transcript.
|
||||||
|
fn write_point(&mut self, point: C) -> io::Result<()>;
|
||||||
|
|
||||||
|
/// Write a scalar to the proof and the transcript.
|
||||||
|
fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()>;
|
||||||
|
|
||||||
|
/// Fork the transcript, creating a variant of this `TranscriptWrite` which
|
||||||
|
/// does not output anything to the writer.
|
||||||
|
fn fork(&self) -> Self::ForkedTranscript;
|
||||||
|
|
||||||
|
/// Return the writer to conclude the interaction and take possession of the
|
||||||
|
/// proof.
|
||||||
|
fn finalize(self) -> W;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is just a simple (and completely broken) transcript reader
|
||||||
|
/// implementation, standing in for some algebraic hash function that we'll
|
||||||
|
/// switch to later.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DummyHash<F: FieldExt> {
|
pub struct DummyHashReader<R: Read, C: CurveAffine> {
|
||||||
power: F,
|
base_state: C::Base,
|
||||||
state: F,
|
scalar_state: C::Scalar,
|
||||||
|
read_scalar: bool,
|
||||||
|
reader: R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> Hasher<F> for DummyHash<F> {
|
impl<R: Read, C: CurveAffine> TranscriptRead<R, C> for DummyHashReader<R, C> {
|
||||||
fn init(key: F) -> Self {
|
fn init(reader: R, key: C::Base) -> Self {
|
||||||
DummyHash {
|
DummyHashReader {
|
||||||
power: F::ZETA + F::one() + key,
|
base_state: key + &C::Base::from_u64(1013),
|
||||||
state: F::ZETA,
|
scalar_state: C::Scalar::from_u64(1013),
|
||||||
|
read_scalar: false,
|
||||||
|
reader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn absorb(&mut self, value: F) {
|
|
||||||
for _ in 0..10 {
|
fn read_point(&mut self) -> io::Result<C> {
|
||||||
self.state += value;
|
let mut compressed = [0u8; 32];
|
||||||
self.state *= self.power;
|
self.reader.read_exact(&mut compressed[..])?;
|
||||||
self.power += self.power.square();
|
let point: C = Option::from(C::from_bytes(&compressed)).ok_or(io::Error::new(
|
||||||
self.state += self.power;
|
io::ErrorKind::Other,
|
||||||
|
"invalid point encoding in proof",
|
||||||
|
))?;
|
||||||
|
self.common_point(point)?;
|
||||||
|
|
||||||
|
Ok(point)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_scalar(&mut self) -> io::Result<C::Scalar> {
|
||||||
|
let mut data = [0u8; 32];
|
||||||
|
self.reader.read_exact(&mut data)?;
|
||||||
|
let scalar = Option::from(C::Scalar::from_bytes(&data)).ok_or(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"invalid field element encoding in proof",
|
||||||
|
))?;
|
||||||
|
self.scalar_state += &(scalar * &C::Scalar::ZETA);
|
||||||
|
self.scalar_state = self.scalar_state.square();
|
||||||
|
self.read_scalar = true;
|
||||||
|
|
||||||
|
Ok(scalar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn squeeze(&mut self) -> F {
|
|
||||||
let tmp = self.state;
|
impl<R: Read, C: CurveAffine> Transcript<C> for DummyHashReader<R, C> {
|
||||||
self.absorb(tmp);
|
fn common_point(&mut self, point: C) -> io::Result<()> {
|
||||||
|
let (x, y) = Option::from(point.get_xy()).ok_or(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"cannot write points at infinity to the transcript",
|
||||||
|
))?;
|
||||||
|
self.base_state += &(x * &C::Base::ZETA);
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
self.base_state += &(y * &C::Base::ZETA);
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn squeeze_challenge(&mut self) -> C::Base {
|
||||||
|
if self.read_scalar {
|
||||||
|
let x = C::Base::from_bytes(&self.scalar_state.to_bytes()).unwrap();
|
||||||
|
self.base_state += &(x * &C::Base::ZETA);
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
self.scalar_state = self.scalar_state.square();
|
||||||
|
self.read_scalar = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp = self.base_state;
|
||||||
|
for _ in 0..5 {
|
||||||
|
self.base_state *= &(C::Base::ZETA + &C::Base::ZETA);
|
||||||
|
self.base_state += &C::Base::ZETA;
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
}
|
||||||
|
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A transcript that can absorb points from both the base field and scalar
|
/// This is just a simple (and completely broken) transcript writer
|
||||||
/// field of a curve
|
/// implementation, standing in for some algebraic hash function that we'll
|
||||||
|
/// switch to later.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Transcript<C: CurveAffine, HBase, HScalar>
|
pub struct DummyHashWriter<W: Write, C: CurveAffine> {
|
||||||
where
|
base_state: C::Base,
|
||||||
HBase: Hasher<C::Base>,
|
scalar_state: C::Scalar,
|
||||||
HScalar: Hasher<C::Scalar>,
|
written_scalar: bool,
|
||||||
{
|
writer: W,
|
||||||
// Hasher over the base field
|
|
||||||
base_hasher: HBase,
|
|
||||||
// Hasher over the scalar field
|
|
||||||
scalar_hasher: HScalar,
|
|
||||||
// Indicates if scalar(s) has been hashed but not squeezed
|
|
||||||
scalar_needs_squeezing: bool,
|
|
||||||
// PhantomData
|
|
||||||
_marker: PhantomData<C>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CurveAffine, HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>
|
impl<W: Write, C: CurveAffine> TranscriptWrite<W, C> for DummyHashWriter<W, C> {
|
||||||
Transcript<C, HBase, HScalar>
|
type ForkedTranscript = DummyHashWriter<io::Sink, C>;
|
||||||
{
|
|
||||||
/// Initialise a new transcript with Field::one() as keys
|
fn init(writer: W, key: C::Base) -> Self {
|
||||||
/// in both the base_hasher and scalar_hasher
|
DummyHashWriter {
|
||||||
pub fn new() -> Self {
|
base_state: key + &C::Base::from_u64(1013),
|
||||||
let base_hasher = HBase::init(C::Base::one());
|
scalar_state: C::Scalar::from_u64(1013),
|
||||||
let scalar_hasher = HScalar::init(C::Scalar::one());
|
written_scalar: false,
|
||||||
Transcript {
|
writer,
|
||||||
base_hasher,
|
}
|
||||||
scalar_hasher,
|
}
|
||||||
scalar_needs_squeezing: false,
|
fn write_point(&mut self, point: C) -> io::Result<()> {
|
||||||
_marker: PhantomData,
|
self.common_point(point)?;
|
||||||
|
let compressed = point.to_bytes();
|
||||||
|
self.writer.write_all(&compressed[..])
|
||||||
|
}
|
||||||
|
fn write_scalar(&mut self, scalar: C::Scalar) -> io::Result<()> {
|
||||||
|
self.scalar_state += &(scalar * &C::Scalar::ZETA);
|
||||||
|
self.scalar_state = self.scalar_state.square();
|
||||||
|
self.written_scalar = true;
|
||||||
|
let data = scalar.to_bytes();
|
||||||
|
self.writer.write_all(&data[..])
|
||||||
|
}
|
||||||
|
fn fork(&self) -> Self::ForkedTranscript {
|
||||||
|
DummyHashWriter {
|
||||||
|
base_state: self.base_state,
|
||||||
|
scalar_state: self.scalar_state,
|
||||||
|
written_scalar: self.written_scalar,
|
||||||
|
writer: io::sink(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn finalize(self) -> W {
|
||||||
|
// TODO: handle outstanding scalars?
|
||||||
|
self.writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conditional_scalar_squeeze(&mut self) {
|
impl<W: Write, C: CurveAffine> Transcript<C> for DummyHashWriter<W, C> {
|
||||||
if self.scalar_needs_squeezing {
|
fn common_point(&mut self, point: C) -> io::Result<()> {
|
||||||
let transcript_scalar_point =
|
let (x, y) = Option::from(point.get_xy()).ok_or(io::Error::new(
|
||||||
C::Base::from_bytes(&(self.scalar_hasher.squeeze()).to_bytes()).unwrap();
|
io::ErrorKind::Other,
|
||||||
self.base_hasher.absorb(transcript_scalar_point);
|
"cannot write points at infinity to the transcript",
|
||||||
self.scalar_needs_squeezing = false;
|
))?;
|
||||||
}
|
self.base_state += &(x * &C::Base::ZETA);
|
||||||
}
|
self.base_state = self.base_state.square();
|
||||||
|
self.base_state += &(y * &C::Base::ZETA);
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
|
||||||
/// Absorb a curve point into the transcript by absorbing
|
|
||||||
/// its x and y coordinates
|
|
||||||
pub fn absorb_point(&mut self, point: &C) -> Result<(), ()> {
|
|
||||||
self.conditional_scalar_squeeze();
|
|
||||||
let tmp = point.get_xy();
|
|
||||||
if bool::from(tmp.is_none()) {
|
|
||||||
return Err(());
|
|
||||||
};
|
|
||||||
let tmp = tmp.unwrap();
|
|
||||||
self.base_hasher.absorb(tmp.0);
|
|
||||||
self.base_hasher.absorb(tmp.1);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb a base into the base_hasher
|
fn squeeze_challenge(&mut self) -> C::Base {
|
||||||
pub fn absorb_base(&mut self, base: C::Base) {
|
if self.written_scalar {
|
||||||
self.conditional_scalar_squeeze();
|
let x = C::Base::from_bytes(&self.scalar_state.to_bytes()).unwrap();
|
||||||
self.base_hasher.absorb(base);
|
self.base_state += &(x * &C::Base::ZETA);
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
|
self.scalar_state = self.scalar_state.square();
|
||||||
|
self.written_scalar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Absorb a scalar into the scalar_hasher
|
let tmp = self.base_state;
|
||||||
pub fn absorb_scalar(&mut self, scalar: C::Scalar) {
|
for _ in 0..5 {
|
||||||
self.scalar_hasher.absorb(scalar);
|
self.base_state *= &(C::Base::ZETA + &C::Base::ZETA);
|
||||||
self.scalar_needs_squeezing = true;
|
self.base_state += &C::Base::ZETA;
|
||||||
|
self.base_state = self.base_state.square();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Squeeze the transcript to obtain a C::Base value.
|
tmp
|
||||||
pub fn squeeze(&mut self) -> C::Base {
|
|
||||||
self.conditional_scalar_squeeze();
|
|
||||||
self.base_hasher.squeeze()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,64 +226,61 @@ pub struct Challenge(pub(crate) u128);
|
||||||
|
|
||||||
impl Challenge {
|
impl Challenge {
|
||||||
/// Obtains a new challenge from the transcript.
|
/// Obtains a new challenge from the transcript.
|
||||||
pub fn get<C, HBase, HScalar>(transcript: &mut Transcript<C, HBase, HScalar>) -> Challenge
|
pub fn get<C: CurveAffine, T: Transcript<C>>(transcript: &mut T) -> Challenge {
|
||||||
where
|
Challenge(transcript.squeeze_challenge().get_lower_128())
|
||||||
C: CurveAffine,
|
|
||||||
HBase: Hasher<C::Base>,
|
|
||||||
HScalar: Hasher<C::Scalar>,
|
|
||||||
{
|
|
||||||
Challenge(transcript.squeeze().get_lower_128())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The scalar representation of a verifier challenge.
|
/// The scalar representation of a verifier challenge.
|
||||||
///
|
///
|
||||||
/// The `T` type can be used to scope the challenge to a specific context, or set to `()`
|
/// The `Type` type can be used to scope the challenge to a specific context, or
|
||||||
/// if no context is required.
|
/// set to `()` if no context is required.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct ChallengeScalar<F: FieldExt, T> {
|
pub struct ChallengeScalar<C: CurveAffine, Type> {
|
||||||
inner: F,
|
inner: C::Scalar,
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, T> From<Challenge> for ChallengeScalar<F, T> {
|
impl<C: CurveAffine, Type> From<Challenge> for ChallengeScalar<C, Type> {
|
||||||
/// This algorithm applies the mapping of Algorithm 1 from the
|
/// This algorithm applies the mapping of Algorithm 1 from the
|
||||||
/// [Halo](https://eprint.iacr.org/2019/1021) paper.
|
/// [Halo](https://eprint.iacr.org/2019/1021) paper.
|
||||||
fn from(challenge: Challenge) -> Self {
|
fn from(challenge: Challenge) -> Self {
|
||||||
let mut acc = (F::ZETA + F::one()).double();
|
let mut acc = (C::Scalar::ZETA + &C::Scalar::one()).double();
|
||||||
|
|
||||||
for i in (0..64).rev() {
|
for i in (0..64).rev() {
|
||||||
let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1;
|
let should_negate = ((challenge.0 >> ((i << 1) + 1)) & 1) == 1;
|
||||||
let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1;
|
let should_endo = ((challenge.0 >> (i << 1)) & 1) == 1;
|
||||||
|
|
||||||
let q = if should_negate { -F::one() } else { F::one() };
|
let q = if should_negate {
|
||||||
let q = if should_endo { q * F::ZETA } else { q };
|
-C::Scalar::one()
|
||||||
acc = acc + q + acc;
|
} else {
|
||||||
|
C::Scalar::one()
|
||||||
|
};
|
||||||
|
let q = if should_endo { q * &C::Scalar::ZETA } else { q };
|
||||||
|
acc = acc + &q + &acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChallengeScalar {
|
ChallengeScalar {
|
||||||
inner: acc,
|
inner: acc,
|
||||||
_marker: PhantomData::default(),
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, T> ChallengeScalar<F, T> {
|
impl<C: CurveAffine, Type> ChallengeScalar<C, Type> {
|
||||||
/// Obtains a new challenge from the transcript.
|
/// Obtains a new challenge from the transcript.
|
||||||
pub fn get<C, HBase, HScalar>(transcript: &mut Transcript<C, HBase, HScalar>) -> Self
|
pub fn get<T: Transcript<C>>(transcript: &mut T) -> Self
|
||||||
where
|
where
|
||||||
C: CurveAffine,
|
C: CurveAffine,
|
||||||
HBase: Hasher<C::Base>,
|
|
||||||
HScalar: Hasher<C::Scalar>,
|
|
||||||
{
|
{
|
||||||
Challenge::get(transcript).into()
|
Challenge::get(transcript).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt, T> Deref for ChallengeScalar<F, T> {
|
impl<C: CurveAffine, Type> Deref for ChallengeScalar<C, Type> {
|
||||||
type Target = F;
|
type Target = C::Scalar;
|
||||||
|
|
||||||
fn deref(&self) -> &F {
|
fn deref(&self) -> &C::Scalar {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue