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