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