arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/pasta.rs
src/pasta.rsRaw
  1//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
  2use crate::{
  3  poseidon::{PoseidonRO, PoseidonROCircuit},
  4  traits::{ChallengeTrait, CompressedGroup, Group},
  5};
  6use digest::{ExtendableOutput, Input};
  7use ff::Field;
  8use merlin::Transcript;
  9use num_bigint::BigInt;
 10use num_traits::Num;
 11use pasta_curves::{
 12  self,
 13  arithmetic::{CurveAffine, CurveExt, Group as OtherGroup},
 14  group::{Curve, Group as AnotherGroup, GroupEncoding},
 15  pallas, vesta, Ep, Eq,
 16};
 17use rand::SeedableRng;
 18use rand_chacha::ChaCha20Rng;
 19use rayon::prelude::*;
 20use sha3::Shake256;
 21use std::{io::Read, ops::Mul};
 22
 23//////////////////////////////////////Pallas///////////////////////////////////////////////
 24
 25/// A wrapper for compressed group elements that come from the pallas curve
 26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 27pub struct PallasCompressedElementWrapper {
 28  repr: [u8; 32],
 29}
 30
 31impl PallasCompressedElementWrapper {
 32  /// Wraps repr into the wrapper
 33  pub fn new(repr: [u8; 32]) -> Self {
 34    Self { repr }
 35  }
 36}
 37
 38impl Group for pallas::Point {
 39  type Base = pallas::Base;
 40  type Scalar = pallas::Scalar;
 41  type CompressedGroupElement = PallasCompressedElementWrapper;
 42  type PreprocessedGroupElement = pallas::Affine;
 43  type RO = PoseidonRO<Self::Base, Self::Scalar>;
 44  type ROCircuit = PoseidonROCircuit<Self::Base>;
 45
 46  fn vartime_multiscalar_mul(
 47    scalars: &[Self::Scalar],
 48    bases: &[Self::PreprocessedGroupElement],
 49  ) -> Self {
 50    if scalars.len() >= 128 {
 51      pasta_msm::pallas(bases, scalars)
 52    } else {
 53      scalars
 54        .par_iter()
 55        .zip(bases)
 56        .map(|(scalar, base)| base.mul(scalar))
 57        .reduce(Ep::group_zero, |x, y| x + y)
 58    }
 59  }
 60
 61  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
 62    self.to_affine()
 63  }
 64
 65  fn compress(&self) -> Self::CompressedGroupElement {
 66    PallasCompressedElementWrapper::new(self.to_bytes())
 67  }
 68
 69  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
 70    let mut shake = Shake256::default();
 71    shake.input(label);
 72    let mut reader = shake.xof_result();
 73    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
 74    let mut uniform_bytes = [0u8; 32];
 75    for _ in 0..n {
 76      reader.read_exact(&mut uniform_bytes).unwrap();
 77      let hash = Ep::hash_to_curve("from_uniform_bytes");
 78      gens.push(hash(&uniform_bytes).to_affine());
 79    }
 80    gens
 81  }
 82
 83  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
 84    let coordinates = self.to_affine().coordinates();
 85    if coordinates.is_some().unwrap_u8() == 1 {
 86      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
 87    } else {
 88      (Self::Base::zero(), Self::Base::zero(), true)
 89    }
 90  }
 91
 92  fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
 93    let A = Self::Base::zero();
 94    let B = Self::Base::from(5);
 95    let order = BigInt::from_str_radix(
 96      "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
 97      16,
 98    )
 99    .unwrap();
100
101    (A, B, order)
102  }
103
104  fn zero() -> Self {
105    pallas::Point::group_zero()
106  }
107
108  fn get_generator() -> Self {
109    pallas::Point::generator()
110  }
111}
112
113impl ChallengeTrait for pallas::Scalar {
114  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
115    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
116    transcript.challenge_bytes(label, &mut key);
117    let mut rng = ChaCha20Rng::from_seed(key);
118    pallas::Scalar::random(&mut rng)
119  }
120}
121
122impl CompressedGroup for PallasCompressedElementWrapper {
123  type GroupElement = pallas::Point;
124
125  fn decompress(&self) -> Option<pallas::Point> {
126    Some(Ep::from_bytes(&self.repr).unwrap())
127  }
128  fn as_bytes(&self) -> &[u8] {
129    &self.repr
130  }
131}
132
133//////////////////////////////////////Vesta////////////////////////////////////////////////
134
135/// A wrapper for compressed group elements that come from the vesta curve
136#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137pub struct VestaCompressedElementWrapper {
138  repr: [u8; 32],
139}
140
141impl VestaCompressedElementWrapper {
142  /// Wraps repr into the wrapper
143  pub fn new(repr: [u8; 32]) -> Self {
144    Self { repr }
145  }
146}
147
148impl Group for vesta::Point {
149  type Base = vesta::Base;
150  type Scalar = vesta::Scalar;
151  type CompressedGroupElement = VestaCompressedElementWrapper;
152  type PreprocessedGroupElement = vesta::Affine;
153  type RO = PoseidonRO<Self::Base, Self::Scalar>;
154  type ROCircuit = PoseidonROCircuit<Self::Base>;
155
156  fn vartime_multiscalar_mul(
157    scalars: &[Self::Scalar],
158    bases: &[Self::PreprocessedGroupElement],
159  ) -> Self {
160    if scalars.len() >= 128 {
161      pasta_msm::vesta(bases, scalars)
162    } else {
163      scalars
164        .par_iter()
165        .zip(bases)
166        .map(|(scalar, base)| base.mul(scalar))
167        .reduce(Eq::group_zero, |x, y| x + y)
168    }
169  }
170
171  fn compress(&self) -> Self::CompressedGroupElement {
172    VestaCompressedElementWrapper::new(self.to_bytes())
173  }
174
175  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
176    self.to_affine()
177  }
178
179  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
180    let mut shake = Shake256::default();
181    shake.input(label);
182    let mut reader = shake.xof_result();
183    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
184    let mut uniform_bytes = [0u8; 32];
185    for _ in 0..n {
186      reader.read_exact(&mut uniform_bytes).unwrap();
187      let hash = Eq::hash_to_curve("from_uniform_bytes");
188      gens.push(hash(&uniform_bytes).to_affine());
189    }
190    gens
191  }
192
193  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
194    let coordinates = self.to_affine().coordinates();
195    if coordinates.is_some().unwrap_u8() == 1 {
196      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
197    } else {
198      (Self::Base::zero(), Self::Base::zero(), true)
199    }
200  }
201
202  fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
203    let A = Self::Base::zero();
204    let B = Self::Base::from(5);
205    let order = BigInt::from_str_radix(
206      "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
207      16,
208    )
209    .unwrap();
210
211    (A, B, order)
212  }
213
214  fn zero() -> Self {
215    vesta::Point::group_zero()
216  }
217
218  fn get_generator() -> Self {
219    vesta::Point::generator()
220  }
221}
222
223impl ChallengeTrait for vesta::Scalar {
224  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
225    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
226    transcript.challenge_bytes(label, &mut key);
227    let mut rng = ChaCha20Rng::from_seed(key);
228    vesta::Scalar::random(&mut rng)
229  }
230}
231
232impl CompressedGroup for VestaCompressedElementWrapper {
233  type GroupElement = vesta::Point;
234
235  fn decompress(&self) -> Option<vesta::Point> {
236    Some(Eq::from_bytes(&self.repr).unwrap())
237  }
238  fn as_bytes(&self) -> &[u8] {
239    &self.repr
240  }
241}