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}