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_order() -> BigInt {
 93    BigInt::from_str_radix(
 94      "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
 95      16,
 96    )
 97    .unwrap()
 98  }
 99
100  fn zero() -> Self {
101    pallas::Point::group_zero()
102  }
103
104  fn get_generator() -> Self {
105    pallas::Point::generator()
106  }
107}
108
109impl ChallengeTrait for pallas::Scalar {
110  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
111    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
112    transcript.challenge_bytes(label, &mut key);
113    let mut rng = ChaCha20Rng::from_seed(key);
114    pallas::Scalar::random(&mut rng)
115  }
116}
117
118impl CompressedGroup for PallasCompressedElementWrapper {
119  type GroupElement = pallas::Point;
120
121  fn decompress(&self) -> Option<pallas::Point> {
122    Some(Ep::from_bytes(&self.repr).unwrap())
123  }
124  fn as_bytes(&self) -> &[u8] {
125    &self.repr
126  }
127}
128
129//////////////////////////////////////Vesta////////////////////////////////////////////////
130
131/// A wrapper for compressed group elements that come from the vesta curve
132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
133pub struct VestaCompressedElementWrapper {
134  repr: [u8; 32],
135}
136
137impl VestaCompressedElementWrapper {
138  /// Wraps repr into the wrapper
139  pub fn new(repr: [u8; 32]) -> Self {
140    Self { repr }
141  }
142}
143
144impl Group for vesta::Point {
145  type Base = vesta::Base;
146  type Scalar = vesta::Scalar;
147  type CompressedGroupElement = VestaCompressedElementWrapper;
148  type PreprocessedGroupElement = vesta::Affine;
149  type RO = PoseidonRO<Self::Base, Self::Scalar>;
150  type ROCircuit = PoseidonROCircuit<Self::Base>;
151
152  fn vartime_multiscalar_mul(
153    scalars: &[Self::Scalar],
154    bases: &[Self::PreprocessedGroupElement],
155  ) -> Self {
156    if scalars.len() >= 128 {
157      pasta_msm::vesta(bases, scalars)
158    } else {
159      scalars
160        .par_iter()
161        .zip(bases)
162        .map(|(scalar, base)| base.mul(scalar))
163        .reduce(Eq::group_zero, |x, y| x + y)
164    }
165  }
166
167  fn compress(&self) -> Self::CompressedGroupElement {
168    VestaCompressedElementWrapper::new(self.to_bytes())
169  }
170
171  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
172    self.to_affine()
173  }
174
175  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
176    let mut shake = Shake256::default();
177    shake.input(label);
178    let mut reader = shake.xof_result();
179    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
180    let mut uniform_bytes = [0u8; 32];
181    for _ in 0..n {
182      reader.read_exact(&mut uniform_bytes).unwrap();
183      let hash = Eq::hash_to_curve("from_uniform_bytes");
184      gens.push(hash(&uniform_bytes).to_affine());
185    }
186    gens
187  }
188
189  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
190    let coordinates = self.to_affine().coordinates();
191    if coordinates.is_some().unwrap_u8() == 1 {
192      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
193    } else {
194      (Self::Base::zero(), Self::Base::zero(), true)
195    }
196  }
197
198  fn get_order() -> BigInt {
199    BigInt::from_str_radix(
200      "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
201      16,
202    )
203    .unwrap()
204  }
205
206  fn zero() -> Self {
207    vesta::Point::group_zero()
208  }
209
210  fn get_generator() -> Self {
211    vesta::Point::generator()
212  }
213}
214
215impl ChallengeTrait for vesta::Scalar {
216  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
217    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
218    transcript.challenge_bytes(label, &mut key);
219    let mut rng = ChaCha20Rng::from_seed(key);
220    vesta::Scalar::random(&mut rng)
221  }
222}
223
224impl CompressedGroup for VestaCompressedElementWrapper {
225  type GroupElement = vesta::Point;
226
227  fn decompress(&self) -> Option<vesta::Point> {
228    Some(Eq::from_bytes(&self.repr).unwrap())
229  }
230  fn as_bytes(&self) -> &[u8] {
231    &self.repr
232  }
233}