sp1_curves/weierstrass/
mod.rs

1use generic_array::GenericArray;
2use num::{BigUint, Zero};
3use serde::{Deserialize, Serialize};
4
5use super::CurveType;
6use crate::{
7    params::{FieldParameters, NumLimbs, NumWords},
8    utils::biguint_to_bits_le,
9    AffinePoint, EllipticCurve, EllipticCurveParameters,
10};
11
12#[cfg(feature = "bigint-rug")]
13use crate::utils::{biguint_to_rug, rug_to_biguint};
14
15pub mod bls12_381;
16pub mod bn254;
17pub mod secp256k1;
18pub mod secp256r1;
19
20/// Parameters that specify a short Weierstrass curve : y^2 = x^3 + ax + b.
21pub trait WeierstrassParameters: EllipticCurveParameters {
22    const A: GenericArray<u8, <Self::BaseField as NumLimbs>::Limbs>;
23    const B: GenericArray<u8, <Self::BaseField as NumLimbs>::Limbs>;
24
25    fn generator() -> (BigUint, BigUint);
26
27    fn prime_group_order() -> BigUint;
28
29    fn a_int() -> BigUint {
30        let mut modulus = BigUint::zero();
31        for (i, limb) in Self::A.iter().enumerate() {
32            modulus += BigUint::from(*limb) << (8 * i);
33        }
34        modulus
35    }
36
37    fn b_int() -> BigUint {
38        let mut modulus = BigUint::zero();
39        for (i, limb) in Self::B.iter().enumerate() {
40            modulus += BigUint::from(*limb) << (8 * i);
41        }
42        modulus
43    }
44
45    fn nb_scalar_bits() -> usize {
46        Self::BaseField::NB_LIMBS * 16
47    }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51pub struct SwCurve<E>(pub E);
52
53impl<E: WeierstrassParameters> WeierstrassParameters for SwCurve<E> {
54    const A: GenericArray<u8, <Self::BaseField as NumLimbs>::Limbs> = E::A;
55    const B: GenericArray<u8, <Self::BaseField as NumLimbs>::Limbs> = E::B;
56
57    fn a_int() -> BigUint {
58        E::a_int()
59    }
60
61    fn b_int() -> BigUint {
62        E::b_int()
63    }
64
65    fn generator() -> (BigUint, BigUint) {
66        E::generator()
67    }
68
69    fn nb_scalar_bits() -> usize {
70        E::nb_scalar_bits()
71    }
72
73    fn prime_group_order() -> BigUint {
74        E::prime_group_order()
75    }
76}
77
78impl<E: WeierstrassParameters> EllipticCurveParameters for SwCurve<E> {
79    type BaseField = E::BaseField;
80
81    const CURVE_TYPE: CurveType = E::CURVE_TYPE;
82}
83
84impl<E: WeierstrassParameters> EllipticCurve for SwCurve<E> {
85    const NB_LIMBS: usize = Self::BaseField::NB_LIMBS;
86    const NB_WITNESS_LIMBS: usize = Self::BaseField::NB_WITNESS_LIMBS;
87
88    fn ec_add(p: &AffinePoint<Self>, q: &AffinePoint<Self>) -> AffinePoint<Self> {
89        p.sw_add(q)
90    }
91
92    fn ec_double(p: &AffinePoint<Self>) -> AffinePoint<Self> {
93        p.sw_double()
94    }
95
96    fn ec_generator() -> AffinePoint<Self> {
97        let (x, y) = E::generator();
98        AffinePoint::new(x, y)
99    }
100
101    fn ec_neutral() -> Option<AffinePoint<Self>> {
102        None
103    }
104
105    fn ec_neg(p: &AffinePoint<Self>) -> AffinePoint<Self> {
106        let modulus = E::BaseField::modulus();
107        AffinePoint::new(p.x.clone(), modulus - &p.y)
108    }
109}
110
111impl<E: WeierstrassParameters> SwCurve<E> {
112    pub fn generator() -> AffinePoint<SwCurve<E>> {
113        let (x, y) = E::generator();
114
115        AffinePoint::new(x, y)
116    }
117
118    pub fn a_int() -> BigUint {
119        E::a_int()
120    }
121
122    pub fn b_int() -> BigUint {
123        E::b_int()
124    }
125}
126
127impl<E: WeierstrassParameters> AffinePoint<SwCurve<E>> {
128    pub fn sw_scalar_mul(&self, scalar: &BigUint) -> Self {
129        let mut result: Option<AffinePoint<SwCurve<E>>> = None;
130        let mut temp = self.clone();
131        let bits = biguint_to_bits_le(scalar, E::nb_scalar_bits());
132        for bit in bits {
133            if bit {
134                result = result.map(|r| r.sw_add(&temp)).or(Some(temp.clone()));
135            }
136            temp = temp.sw_double();
137        }
138        result.unwrap()
139    }
140}
141
142pub fn biguint_to_dashu(integer: &BigUint) -> dashu::integer::UBig {
143    dashu::integer::UBig::from_le_bytes(integer.to_bytes_le().as_slice())
144}
145
146pub fn dashu_to_biguint(integer: &dashu::integer::UBig) -> BigUint {
147    BigUint::from_bytes_le(&integer.to_le_bytes())
148}
149
150pub fn dashu_modpow(
151    base: &dashu::integer::UBig,
152    exponent: &dashu::integer::UBig,
153    modulus: &dashu::integer::UBig,
154) -> dashu::integer::UBig {
155    if modulus == &dashu::integer::UBig::from(1u32) {
156        return dashu::integer::UBig::from(0u32);
157    }
158
159    let mut result = dashu::integer::UBig::from(1u32);
160    let mut base = base.clone() % modulus;
161    let mut exp = exponent.clone();
162
163    while exp > dashu::integer::UBig::from(0u32) {
164        if &exp % dashu::integer::UBig::from(2u32) == dashu::integer::UBig::from(1u32) {
165            result = (result * &base) % modulus;
166        }
167        exp >>= 1;
168        base = (&base * &base) % modulus;
169    }
170
171    result
172}
173
174impl<E: WeierstrassParameters> AffinePoint<SwCurve<E>> {
175    pub fn sw_add(&self, other: &AffinePoint<SwCurve<E>>) -> AffinePoint<SwCurve<E>> {
176        if self.x == other.x && self.y == other.y {
177            panic!("Error: Points are the same. Use sw_double instead.");
178        }
179
180        cfg_if::cfg_if! {
181            if #[cfg(feature = "bigint-rug")] {
182                self.sw_add_rug(other)
183            } else {
184                let p = biguint_to_dashu(&E::BaseField::modulus());
185                let self_x = biguint_to_dashu(&self.x);
186                let self_y = biguint_to_dashu(&self.y);
187                let other_x = biguint_to_dashu(&other.x);
188                let other_y = biguint_to_dashu(&other.y);
189
190                let slope_numerator = (&p + &other_y - &self_y) % &p;
191                let slope_denominator = (&p + &other_x - &self_x) % &p;
192                let slope_denom_inverse =
193                    dashu_modpow(&slope_denominator, &(&p - &dashu::integer::UBig::from(2u32)), &p);
194                let slope = (slope_numerator * &slope_denom_inverse) % &p;
195
196                let x_3n = (&slope * &slope + &p + &p - &self_x - &other_x) % &p;
197                let y_3n = (&slope * &(&p + &self_x - &x_3n) + &p - &self_y) % &p;
198
199                AffinePoint::new(dashu_to_biguint(&x_3n), dashu_to_biguint(&y_3n))
200            }
201        }
202    }
203
204    pub fn sw_double(&self) -> AffinePoint<SwCurve<E>> {
205        cfg_if::cfg_if! {
206            if #[cfg(feature = "bigint-rug")] {
207                self.sw_double_rug()
208            } else {
209                let p = biguint_to_dashu(&E::BaseField::modulus());
210                let a = biguint_to_dashu(&E::a_int());
211
212                let self_x = biguint_to_dashu(&self.x);
213                let self_y = biguint_to_dashu(&self.y);
214
215                let slope_numerator = (&a + &(&self_x * &self_x) * 3u32) % &p;
216
217                let slope_denominator = (&self_y * 2u32) % &p;
218                let slope_denom_inverse =
219                    dashu_modpow(&slope_denominator, &(&p - &dashu::integer::UBig::from(2u32)), &p);
220                // let slope_denom_inverse = slope_denominator.modpow(&(&p - 2u32), &p);
221                let slope = (slope_numerator * &slope_denom_inverse) % &p;
222
223                let x_3n = (&slope * &slope + &p + &p - &self_x - &self_x) % &p;
224
225                let y_3n = (&slope * &(&p + &self_x - &x_3n) + &p - &self_y) % &p;
226
227                AffinePoint::new(dashu_to_biguint(&x_3n), dashu_to_biguint(&y_3n))
228            }
229        }
230    }
231
232    #[cfg(feature = "bigint-rug")]
233    pub fn sw_add_rug(&self, other: &AffinePoint<SwCurve<E>>) -> AffinePoint<SwCurve<E>> {
234        use rug::Complete;
235        let p = biguint_to_rug(&E::BaseField::modulus());
236        let self_x = biguint_to_rug(&self.x);
237        let self_y = biguint_to_rug(&self.y);
238        let other_x = biguint_to_rug(&other.x);
239        let other_y = biguint_to_rug(&other.y);
240
241        let slope_numerator = ((&p + &other_y).complete() - &self_y) % &p;
242        let slope_denominator = ((&p + &other_x).complete() - &self_x) % &p;
243        let slope_denom_inverse = slope_denominator
244            .pow_mod_ref(&(&p - &rug::Integer::from(2u32)).complete(), &p)
245            .unwrap()
246            .complete();
247        let slope = (slope_numerator * &slope_denom_inverse) % &p;
248
249        let x_3n = ((&slope * &slope + &p).complete() + &p - &self_x - &other_x) % &p;
250        let y_3n = ((&slope * &((&p + &self_x).complete() - &x_3n) + &p).complete() - &self_y) % &p;
251
252        AffinePoint::new(rug_to_biguint(&x_3n), rug_to_biguint(&y_3n))
253    }
254
255    #[cfg(feature = "bigint-rug")]
256    pub fn sw_double_rug(&self) -> AffinePoint<SwCurve<E>> {
257        use rug::Complete;
258        let p = biguint_to_rug(&E::BaseField::modulus());
259        let a = biguint_to_rug(&E::a_int());
260
261        let self_x = biguint_to_rug(&self.x);
262        let self_y = biguint_to_rug(&self.y);
263
264        let slope_numerator = (&a + &(&self_x * &self_x).complete() * 3u32).complete() % &p;
265
266        let slope_denominator = (&self_y * 2u32).complete() % &p;
267        let slope_denom_inverse = slope_denominator
268            .pow_mod_ref(&(&p - &rug::Integer::from(2u32)).complete(), &p)
269            .unwrap()
270            .complete();
271
272        let slope = (slope_numerator * &slope_denom_inverse) % &p;
273
274        let x_3n = ((&slope * &slope + &p).complete() + ((&p - &self_x).complete() - &self_x)) % &p;
275
276        let y_3n = ((&slope * &((&p + &self_x).complete() - &x_3n) + &p).complete() - &self_y) % &p;
277
278        AffinePoint::new(rug_to_biguint(&x_3n), rug_to_biguint(&y_3n))
279    }
280}
281
282#[derive(Debug)]
283pub enum FieldType {
284    Bls12381,
285    Bn254,
286}
287
288pub trait FpOpField: FieldParameters + NumWords {
289    const FIELD_TYPE: FieldType;
290}
291
292#[cfg(test)]
293mod tests {
294
295    use num::bigint::RandBigInt;
296    use rand::thread_rng;
297
298    use super::bn254;
299
300    #[test]
301    fn test_weierstrass_biguint_scalar_mul() {
302        type E = bn254::Bn254;
303        let base = E::generator();
304
305        let mut rng = thread_rng();
306        for _ in 0..10 {
307            let x = rng.gen_biguint(24);
308            let y = rng.gen_biguint(25);
309
310            let x_base = base.sw_scalar_mul(&x);
311            let y_x_base = x_base.sw_scalar_mul(&y);
312            let xy = &x * &y;
313            let xy_base = base.sw_scalar_mul(&xy);
314            assert_eq!(y_x_base, xy_base);
315        }
316    }
317}