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