sp1_curves/weierstrass/
bls12_381.rs

1use amcl::bls381::{big::Big, bls381::utils::deserialize_g1, fp::FP};
2use generic_array::GenericArray;
3use num::{BigUint, Num, Zero};
4use serde::{Deserialize, Serialize};
5use typenum::{U48, U94};
6
7use super::{FieldType, FpOpField, SwCurve, WeierstrassParameters};
8use crate::{
9    params::{FieldParameters, NumLimbs},
10    CurveType, EllipticCurveParameters,
11};
12
13/// Bls12-381 curve parameter
14use crate::{AffinePoint, EllipticCurve};
15
16// Serialization flags
17const COMPRESSION_FLAG: u8 = 0b_1000_0000;
18const Y_IS_ODD_FLAG: u8 = 0b_0010_0000;
19
20#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
21/// Bls12381 curve parameter
22pub struct Bls12381Parameters;
23
24pub type Bls12381 = SwCurve<Bls12381Parameters>;
25
26#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)]
27/// Bls12381 base field parameter
28pub struct Bls12381BaseField;
29
30impl FieldParameters for Bls12381BaseField {
31    // The modulus has been taken from py_ecc python library by Ethereum Foundation.
32    // // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/fields/field_properties.py#L30
33    // The below value is the little-endian representation of the modulus.
34    const NB_LIMBS: usize = 48;
35
36    const MODULUS: &'static [u8] = &[
37        171, 170, 255, 255, 255, 255, 254, 185, 255, 255, 83, 177, 254, 255, 171, 30, 36, 246, 176,
38        246, 160, 210, 48, 103, 191, 18, 133, 243, 132, 75, 119, 100, 215, 172, 75, 67, 182, 167,
39        27, 75, 154, 230, 127, 57, 234, 17, 1, 26,
40    ];
41
42    // A rough witness-offset estimate given the size of the limbs and the size of the field.
43    const WITNESS_OFFSET: usize = 1usize << 15;
44
45    fn modulus() -> BigUint {
46        BigUint::from_str_radix(
47            "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787",
48            10,
49        )
50        .unwrap()
51    }
52}
53
54impl FpOpField for Bls12381BaseField {
55    const FIELD_TYPE: FieldType = FieldType::Bls12381;
56}
57
58impl NumLimbs for Bls12381BaseField {
59    type Limbs = U48;
60    type Witness = U94;
61}
62
63impl EllipticCurveParameters for Bls12381Parameters {
64    type BaseField = Bls12381BaseField;
65    const CURVE_TYPE: CurveType = CurveType::Bls12381;
66}
67
68impl WeierstrassParameters for Bls12381Parameters {
69    // The values of `A` and `B` has been taken from py_ecc python library by Ethereum Foundation.
70    // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/bls12_381/bls12_381_curve.py#L31
71    const A: GenericArray<u8, U48> = GenericArray::from_array([
72        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74    ]);
75
76    const B: GenericArray<u8, U48> = GenericArray::from_array([
77        4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
78        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79    ]);
80
81    fn generator() -> (BigUint, BigUint) {
82        let x = BigUint::from_str_radix(
83            "3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507",
84            10,
85        )
86        .unwrap();
87        let y = BigUint::from_str_radix(
88            "1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569",
89            10,
90        )
91        .unwrap();
92        (x, y)
93    }
94
95    // The prime group order has been taken from py_ecc python library by Ethereum Foundation.
96    // https://github.com/ethereum/py_ecc/blob/7b9e1b3/py_ecc/bls12_381/bls12_381_curve.py#L21-L23
97    fn prime_group_order() -> num::BigUint {
98        BigUint::from_str_radix(
99            "52435875175126190479447740508185965837690552500527637822603658699938581184513",
100            10,
101        )
102        .unwrap()
103    }
104
105    fn a_int() -> BigUint {
106        BigUint::zero()
107    }
108
109    fn b_int() -> BigUint {
110        BigUint::from(4u32)
111    }
112}
113
114pub fn bls12381_decompress<E: EllipticCurve>(bytes_be: &[u8], sign_bit: u32) -> AffinePoint<E> {
115    let mut g1_bytes_be: [u8; 48] = bytes_be.try_into().unwrap();
116    let mut flags = COMPRESSION_FLAG;
117    if sign_bit == 1 {
118        flags |= Y_IS_ODD_FLAG;
119    };
120
121    // set sign and compression flag
122    g1_bytes_be[0] |= flags;
123    let point = deserialize_g1(&g1_bytes_be).unwrap();
124
125    let x_str = point.getx().to_string();
126    let x = BigUint::from_str_radix(x_str.as_str(), 16).unwrap();
127    let y_str = point.gety().to_string();
128    let y = BigUint::from_str_radix(y_str.as_str(), 16).unwrap();
129
130    AffinePoint::new(x, y)
131}
132
133pub fn bls12381_sqrt(a: &BigUint) -> BigUint {
134    let a_big = Big::from_bytes(a.to_bytes_be().as_slice());
135
136    let a_sqrt = FP::new_big(a_big).sqrt();
137
138    BigUint::from_str_radix(a_sqrt.to_string().as_str(), 16).unwrap()
139}
140
141#[cfg(test)]
142mod tests {
143
144    use amcl::bls381::bls381::proof_of_possession::G1_BYTES;
145
146    use super::*;
147    use crate::utils::biguint_from_limbs;
148    use num::bigint::RandBigInt;
149    use rand::thread_rng;
150
151    const NUM_TEST_CASES: usize = 10;
152
153    #[test]
154    fn test_weierstrass_biguint_scalar_mul() {
155        assert_eq!(biguint_from_limbs(Bls12381BaseField::MODULUS), Bls12381BaseField::modulus());
156    }
157
158    #[test]
159    fn test_bls12381_decompress() {
160        // This test checks that decompression of generator, 2x generator, 4x generator, etc. works.
161
162        // Get the generator point.
163        let mut point = {
164            let (x, y) = Bls12381Parameters::generator();
165            AffinePoint::<SwCurve<Bls12381Parameters>>::new(x, y)
166        };
167        for _ in 0..NUM_TEST_CASES {
168            let (compressed_point, is_odd) = {
169                let mut result = [0u8; G1_BYTES];
170                let x = point.x.to_bytes_le();
171                result[..x.len()].copy_from_slice(&x);
172                result.reverse();
173
174                // Evaluate if y > -y
175                let y = point.y.clone();
176                let y_neg = Bls12381BaseField::modulus() - y.clone();
177
178                // Set flags
179                let mut is_odd = 0;
180                if y > y_neg {
181                    result[0] += Y_IS_ODD_FLAG;
182                    is_odd = 1;
183                }
184                result[0] += COMPRESSION_FLAG;
185
186                (result, is_odd)
187            };
188            assert_eq!(point, bls12381_decompress(&compressed_point, is_odd));
189
190            // Double the point to create a "random" point for the next iteration.
191            point = point.clone().sw_double();
192        }
193    }
194
195    #[test]
196    fn test_bls12381_sqrt() {
197        let mut rng = thread_rng();
198        for _ in 0..NUM_TEST_CASES {
199            // Check that sqrt(x^2)^2 == x^2
200            // We use x^2 since not all field elements have a square root
201            let x = rng.gen_biguint(256) % Bls12381BaseField::modulus();
202            let x_2 = (&x * &x) % Bls12381BaseField::modulus();
203            let sqrt = bls12381_sqrt(&x_2);
204            let sqrt_2 = (&sqrt * &sqrt) % Bls12381BaseField::modulus();
205            assert_eq!(sqrt_2, x_2);
206        }
207    }
208
209    #[test]
210    fn test_bls12381_params() {
211        use crate::params::FieldParameters;
212
213        assert_eq!(
214            Bls12381BaseField::modulus(),
215            BigUint::from_bytes_le(<Bls12381BaseField as FieldParameters>::MODULUS)
216        );
217    }
218}