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 core::ops::Mul;
  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 Grp},
 14  group::{Curve, GroupEncoding},
 15  pallas, vesta, Ep, Eq,
 16};
 17use rand::SeedableRng;
 18use rand_chacha::ChaCha20Rng;
 19
 20//////////////////////////////////////Pallas///////////////////////////////////////////////
 21
 22/// A wrapper for compressed group elements that come from the pallas curve
 23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 24pub struct PallasCompressedElementWrapper {
 25  repr: [u8; 32],
 26}
 27
 28impl PallasCompressedElementWrapper {
 29  /// Wraps repr into the wrapper
 30  pub fn new(repr: [u8; 32]) -> Self {
 31    Self { repr }
 32  }
 33}
 34
 35impl Group for pallas::Point {
 36  type Base = pallas::Base;
 37  type Scalar = pallas::Scalar;
 38  type CompressedGroupElement = PallasCompressedElementWrapper;
 39  type PreprocessedGroupElement = pallas::Affine;
 40  type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
 41
 42  fn vartime_multiscalar_mul(
 43    scalars: &[Self::Scalar],
 44    bases: &[Self::PreprocessedGroupElement],
 45  ) -> Self {
 46    // Unoptimized.
 47    scalars
 48      .iter()
 49      .zip(bases)
 50      .map(|(scalar, base)| base.mul(scalar))
 51      .fold(Ep::group_zero(), |acc, x| acc + x)
 52  }
 53
 54  fn compress(&self) -> Self::CompressedGroupElement {
 55    PallasCompressedElementWrapper::new(self.to_bytes())
 56  }
 57
 58  fn from_uniform_bytes(bytes: &[u8]) -> Option<Self::PreprocessedGroupElement> {
 59    if bytes.len() != 64 {
 60      None
 61    } else {
 62      let mut arr = [0; 32];
 63      arr.copy_from_slice(&bytes[0..32]);
 64
 65      let hash = Ep::hash_to_curve("from_uniform_bytes");
 66      Some(hash(&arr).to_affine())
 67    }
 68  }
 69
 70  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
 71    let coordinates = self.to_affine().coordinates();
 72    if coordinates.is_some().unwrap_u8() == 1 {
 73      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
 74    } else {
 75      (Self::Base::zero(), Self::Base::zero(), true)
 76    }
 77  }
 78
 79  fn get_order() -> BigInt {
 80    BigInt::from_str_radix(
 81      "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
 82      16,
 83    )
 84    .unwrap()
 85  }
 86}
 87
 88impl ChallengeTrait for pallas::Scalar {
 89  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
 90    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
 91    transcript.challenge_bytes(label, &mut key);
 92    let mut rng = ChaCha20Rng::from_seed(key);
 93    pallas::Scalar::random(&mut rng)
 94  }
 95}
 96
 97impl CompressedGroup for PallasCompressedElementWrapper {
 98  type GroupElement = pallas::Point;
 99
100  fn decompress(&self) -> Option<pallas::Point> {
101    Some(Ep::from_bytes(&self.repr).unwrap())
102  }
103  fn as_bytes(&self) -> &[u8] {
104    &self.repr
105  }
106}
107
108//////////////////////////////////////Vesta////////////////////////////////////////////////
109
110/// A wrapper for compressed group elements that come from the vesta curve
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
112pub struct VestaCompressedElementWrapper {
113  repr: [u8; 32],
114}
115
116impl VestaCompressedElementWrapper {
117  /// Wraps repr into the wrapper
118  pub fn new(repr: [u8; 32]) -> Self {
119    Self { repr }
120  }
121}
122
123impl Group for vesta::Point {
124  type Base = vesta::Base;
125  type Scalar = vesta::Scalar;
126  type CompressedGroupElement = VestaCompressedElementWrapper;
127  type PreprocessedGroupElement = vesta::Affine;
128  type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
129
130  fn vartime_multiscalar_mul(
131    scalars: &[Self::Scalar],
132    bases: &[Self::PreprocessedGroupElement],
133  ) -> Self {
134    // Unoptimized.
135    scalars
136      .iter()
137      .zip(bases)
138      .map(|(scalar, base)| base.mul(scalar))
139      .fold(Eq::group_zero(), |acc, x| acc + x)
140  }
141
142  fn compress(&self) -> Self::CompressedGroupElement {
143    VestaCompressedElementWrapper::new(self.to_bytes())
144  }
145
146  fn from_uniform_bytes(bytes: &[u8]) -> Option<Self::PreprocessedGroupElement> {
147    if bytes.len() != 64 {
148      None
149    } else {
150      let mut arr = [0; 32];
151      arr.copy_from_slice(&bytes[0..32]);
152
153      let hash = Eq::hash_to_curve("from_uniform_bytes");
154      Some(hash(&arr).to_affine())
155    }
156  }
157
158  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
159    let coordinates = self.to_affine().coordinates();
160    if coordinates.is_some().unwrap_u8() == 1 {
161      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
162    } else {
163      (Self::Base::zero(), Self::Base::zero(), true)
164    }
165  }
166
167  fn get_order() -> BigInt {
168    BigInt::from_str_radix(
169      "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
170      16,
171    )
172    .unwrap()
173  }
174}
175
176impl ChallengeTrait for vesta::Scalar {
177  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
178    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
179    transcript.challenge_bytes(label, &mut key);
180    let mut rng = ChaCha20Rng::from_seed(key);
181    vesta::Scalar::random(&mut rng)
182  }
183}
184
185impl CompressedGroup for VestaCompressedElementWrapper {
186  type GroupElement = vesta::Point;
187
188  fn decompress(&self) -> Option<vesta::Point> {
189    Some(Eq::from_bytes(&self.repr).unwrap())
190  }
191  fn as_bytes(&self) -> &[u8] {
192    &self.repr
193  }
194}