arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/gadgets/ecc.rs
src/gadgets/ecc.rsRaw
  1#![allow(non_snake_case)]
  2use ff::{PrimeField, PrimeFieldBits};
  3use rand::rngs::OsRng;
  4use std::marker::PhantomData;
  5
  6#[derive(Debug, Clone)]
  7pub struct Point<Fp, Fq>
  8where
  9  Fp: PrimeField,
 10  Fq: PrimeField + PrimeFieldBits,
 11{
 12  pub(crate) x: Fp, //TODO: Make this not public
 13  pub(crate) y: Fp,
 14  is_infinity: bool,
 15  _p: PhantomData<Fq>,
 16}
 17
 18impl<Fp, Fq> Point<Fp, Fq>
 19where
 20  Fp: PrimeField,
 21  Fq: PrimeField + PrimeFieldBits,
 22{
 23  #[allow(dead_code)]
 24  pub fn new(x: Fp, y: Fp, is_infinity: bool) -> Self {
 25    Self {
 26      x,
 27      y,
 28      is_infinity,
 29      _p: Default::default(),
 30    }
 31  }
 32
 33  #[allow(dead_code)]
 34  pub fn random_vartime() -> Self {
 35    loop {
 36      let x = Fp::random(&mut OsRng);
 37      let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
 38      if y.is_some().unwrap_u8() == 1 {
 39        return Self {
 40          x,
 41          y: y.unwrap(),
 42          is_infinity: false,
 43          _p: Default::default(),
 44        };
 45      }
 46    }
 47  }
 48
 49  pub fn add(&self, other: &Point<Fp, Fq>) -> Self {
 50    if self.is_infinity {
 51      return other.clone();
 52    }
 53
 54    if other.is_infinity {
 55      return self.clone();
 56    }
 57
 58    let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
 59    let x = lambda * lambda - self.x - other.x;
 60    let y = lambda * (self.x - x) - self.y;
 61    Self {
 62      x,
 63      y,
 64      is_infinity: false,
 65      _p: Default::default(),
 66    }
 67  }
 68
 69  pub fn double(&self) -> Self {
 70    if self.is_infinity {
 71      return Self {
 72        x: Fp::zero(),
 73        y: Fp::zero(),
 74        is_infinity: true,
 75        _p: Default::default(),
 76      };
 77    }
 78
 79    let lambda = (Fp::one() + Fp::one() + Fp::one())
 80      * self.x
 81      * self.x
 82      * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
 83    let x = lambda * lambda - self.x - self.x;
 84    let y = lambda * (self.x - x) - self.y;
 85    Self {
 86      x,
 87      y,
 88      is_infinity: false,
 89      _p: Default::default(),
 90    }
 91  }
 92
 93  #[allow(dead_code)]
 94  pub fn scalar_mul_mont(&self, scalar: &Fq) -> Self {
 95    let mut R0 = Self {
 96      x: Fp::zero(),
 97      y: Fp::zero(),
 98      is_infinity: true,
 99      _p: Default::default(),
100    };
101
102    let mut R1 = self.clone();
103    let bits = scalar.to_le_bits();
104    for i in (0..bits.len()).rev() {
105      if bits[i] {
106        R0 = R0.add(&R1);
107        R1 = R1.double();
108      } else {
109        R1 = R0.add(&R1);
110        R0 = R0.double();
111      }
112    }
113    R0
114  }
115
116  #[allow(dead_code)]
117  pub fn scalar_mul(&self, scalar: &Fq) -> Self {
118    let mut res = Self {
119      x: Fp::zero(),
120      y: Fp::zero(),
121      is_infinity: true,
122      _p: Default::default(),
123    };
124
125    let bits = scalar.to_le_bits();
126    for i in (0..bits.len()).rev() {
127      res = res.double();
128      if bits[i] {
129        res = self.add(&res);
130      }
131    }
132    res
133  }
134}
135
136#[cfg(test)]
137#[allow(clippy::too_many_arguments)]
138mod fp {
139  use ff::PrimeField;
140
141  #[derive(PrimeField)]
142  #[PrimeFieldModulus = "28948022309329048855892746252171976963363056481941560715954676764349967630337"]
143  #[PrimeFieldGenerator = "5"]
144  #[PrimeFieldReprEndianness = "little"]
145  pub struct Fp([u64; 4]);
146}
147
148#[cfg(test)]
149#[allow(clippy::too_many_arguments)]
150mod fq {
151  use ff::PrimeField;
152
153  #[derive(PrimeField)]
154  #[PrimeFieldModulus = "28948022309329048855892746252171976963363056481941647379679742748393362948097"]
155  #[PrimeFieldGenerator = "5"]
156  #[PrimeFieldReprEndianness = "little"]
157  pub struct Fq([u64; 4]);
158}
159
160#[cfg(test)]
161mod tests {
162  use super::*;
163  use super::{fp::Fp, fq::Fq};
164  use ff::Field;
165  use pasta_curves::arithmetic::CurveAffine;
166  use pasta_curves::group::Curve;
167  use pasta_curves::EpAffine;
168  use std::ops::Mul;
169
170  #[test]
171  fn test_ecc_ops() {
172    // perform some curve arithmetic
173    let a = Point::<Fp, Fq>::random_vartime();
174    let b = Point::<Fp, Fq>::random_vartime();
175    let c = a.add(&b);
176    let d = a.double();
177    let s = Fq::random(&mut OsRng);
178    let e = a.scalar_mul(&s);
179
180    // perform the same computation by translating to pasta_curve types
181    let a_pasta = EpAffine::from_xy(
182      pasta_curves::Fp::from_repr(a.x.to_repr().0).unwrap(),
183      pasta_curves::Fp::from_repr(a.y.to_repr().0).unwrap(),
184    )
185    .unwrap();
186    let b_pasta = EpAffine::from_xy(
187      pasta_curves::Fp::from_repr(b.x.to_repr().0).unwrap(),
188      pasta_curves::Fp::from_repr(b.y.to_repr().0).unwrap(),
189    )
190    .unwrap();
191    let c_pasta = (a_pasta + b_pasta).to_affine();
192    let d_pasta = (a_pasta + a_pasta).to_affine();
193    let e_pasta = a_pasta
194      .mul(pasta_curves::Fq::from_repr(s.to_repr().0).unwrap())
195      .to_affine();
196
197    // transform c, d, and e into pasta_curve types
198    let c_pasta_2 = EpAffine::from_xy(
199      pasta_curves::Fp::from_repr(c.x.to_repr().0).unwrap(),
200      pasta_curves::Fp::from_repr(c.y.to_repr().0).unwrap(),
201    )
202    .unwrap();
203    let d_pasta_2 = EpAffine::from_xy(
204      pasta_curves::Fp::from_repr(d.x.to_repr().0).unwrap(),
205      pasta_curves::Fp::from_repr(d.y.to_repr().0).unwrap(),
206    )
207    .unwrap();
208    let e_pasta_2 = EpAffine::from_xy(
209      pasta_curves::Fp::from_repr(e.x.to_repr().0).unwrap(),
210      pasta_curves::Fp::from_repr(e.y.to_repr().0).unwrap(),
211    )
212    .unwrap();
213
214    // check that we have the same outputs
215    assert_eq!(c_pasta, c_pasta_2);
216    assert_eq!(d_pasta, d_pasta_2);
217    assert_eq!(e_pasta, e_pasta_2);
218  }
219}