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,
  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, 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 HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
 44
 45  fn vartime_multiscalar_mul(
 46    scalars: &[Self::Scalar],
 47    bases: &[Self::PreprocessedGroupElement],
 48  ) -> Self {
 49    if scalars.len() >= 128 {
 50      pasta_msm::pallas(bases, scalars)
 51    } else {
 52      scalars
 53        .par_iter()
 54        .zip(bases)
 55        .map(|(scalar, base)| base.mul(scalar))
 56        .reduce(Ep::group_zero, |x, y| x + y)
 57    }
 58  }
 59
 60  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
 61    self.to_affine()
 62  }
 63
 64  fn compress(&self) -> Self::CompressedGroupElement {
 65    PallasCompressedElementWrapper::new(self.to_bytes())
 66  }
 67
 68  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
 69    let mut shake = Shake256::default();
 70    shake.input(label);
 71    let mut reader = shake.xof_result();
 72    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
 73    let mut uniform_bytes = [0u8; 32];
 74    for _ in 0..n {
 75      reader.read_exact(&mut uniform_bytes).unwrap();
 76      let hash = Ep::hash_to_curve("from_uniform_bytes");
 77      gens.push(hash(&uniform_bytes).to_affine());
 78    }
 79    gens
 80  }
 81
 82  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
 83    let coordinates = self.to_affine().coordinates();
 84    if coordinates.is_some().unwrap_u8() == 1 {
 85      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
 86    } else {
 87      (Self::Base::zero(), Self::Base::zero(), true)
 88    }
 89  }
 90
 91  fn get_order() -> BigInt {
 92    BigInt::from_str_radix(
 93      "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
 94      16,
 95    )
 96    .unwrap()
 97  }
 98}
 99
100impl ChallengeTrait for pallas::Scalar {
101  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
102    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
103    transcript.challenge_bytes(label, &mut key);
104    let mut rng = ChaCha20Rng::from_seed(key);
105    pallas::Scalar::random(&mut rng)
106  }
107}
108
109impl CompressedGroup for PallasCompressedElementWrapper {
110  type GroupElement = pallas::Point;
111
112  fn decompress(&self) -> Option<pallas::Point> {
113    Some(Ep::from_bytes(&self.repr).unwrap())
114  }
115  fn as_bytes(&self) -> &[u8] {
116    &self.repr
117  }
118}
119
120//////////////////////////////////////Vesta////////////////////////////////////////////////
121
122/// A wrapper for compressed group elements that come from the vesta curve
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub struct VestaCompressedElementWrapper {
125  repr: [u8; 32],
126}
127
128impl VestaCompressedElementWrapper {
129  /// Wraps repr into the wrapper
130  pub fn new(repr: [u8; 32]) -> Self {
131    Self { repr }
132  }
133}
134
135impl Group for vesta::Point {
136  type Base = vesta::Base;
137  type Scalar = vesta::Scalar;
138  type CompressedGroupElement = VestaCompressedElementWrapper;
139  type PreprocessedGroupElement = vesta::Affine;
140  type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
141
142  fn vartime_multiscalar_mul(
143    scalars: &[Self::Scalar],
144    bases: &[Self::PreprocessedGroupElement],
145  ) -> Self {
146    if scalars.len() >= 128 {
147      pasta_msm::vesta(bases, scalars)
148    } else {
149      scalars
150        .par_iter()
151        .zip(bases)
152        .map(|(scalar, base)| base.mul(scalar))
153        .reduce(Eq::group_zero, |x, y| x + y)
154    }
155  }
156
157  fn compress(&self) -> Self::CompressedGroupElement {
158    VestaCompressedElementWrapper::new(self.to_bytes())
159  }
160
161  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
162    self.to_affine()
163  }
164
165  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
166    let mut shake = Shake256::default();
167    shake.input(label);
168    let mut reader = shake.xof_result();
169    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
170    let mut uniform_bytes = [0u8; 32];
171    for _ in 0..n {
172      reader.read_exact(&mut uniform_bytes).unwrap();
173      let hash = Eq::hash_to_curve("from_uniform_bytes");
174      gens.push(hash(&uniform_bytes).to_affine());
175    }
176    gens
177  }
178
179  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
180    let coordinates = self.to_affine().coordinates();
181    if coordinates.is_some().unwrap_u8() == 1 {
182      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
183    } else {
184      (Self::Base::zero(), Self::Base::zero(), true)
185    }
186  }
187
188  fn get_order() -> BigInt {
189    BigInt::from_str_radix(
190      "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
191      16,
192    )
193    .unwrap()
194  }
195}
196
197impl ChallengeTrait for vesta::Scalar {
198  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
199    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
200    transcript.challenge_bytes(label, &mut key);
201    let mut rng = ChaCha20Rng::from_seed(key);
202    vesta::Scalar::random(&mut rng)
203  }
204}
205
206impl CompressedGroup for VestaCompressedElementWrapper {
207  type GroupElement = vesta::Point;
208
209  fn decompress(&self) -> Option<vesta::Point> {
210    Some(Eq::from_bytes(&self.repr).unwrap())
211  }
212  fn as_bytes(&self) -> &[u8] {
213    &self.repr
214  }
215}