halo2curves/bn256/
curve.rs

1use core::{
2    cmp,
3    fmt::Debug,
4    iter::Sum,
5    ops::{Add, Mul, Neg, Sub},
6};
7use std::convert::TryInto;
8
9use rand::RngCore;
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
11
12use crate::{
13    arithmetic::{mul_512, sbb, CurveEndo, EndoParameters},
14    bn256::{Fq, Fq2, Fr},
15    endo,
16    ff::{Field, PrimeField, WithSmallOrderMulGroup},
17    group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Curve, Group, GroupEncoding},
18    impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative,
19    impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt,
20};
21
22impl crate::serde::endian::EndianRepr for Fq2 {
23    const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN;
24
25    fn to_bytes(&self) -> Vec<u8> {
26        self.to_bytes().to_vec()
27    }
28
29    fn from_bytes(bytes: &[u8]) -> subtle::CtOption<Self> {
30        Fq2::from_bytes(bytes[..Fq2::SIZE].try_into().unwrap())
31    }
32}
33
34new_curve_impl!(
35    (pub),
36    G1,
37    G1Affine,
38    Fq,
39    Fr,
40    (G1_GENERATOR_X,G1_GENERATOR_Y),
41    G1_A,
42    G1_B,
43    "bn256_g1",
44    |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()),
45    crate::serde::CompressedFlagConfig::TwoSpare,
46    standard_sign
47);
48
49new_curve_impl!(
50    (pub),
51    G2,
52    G2Affine,
53    Fq2,
54    Fr,
55    (G2_GENERATOR_X, G2_GENERATOR_Y),
56    G2_A,
57    G2_B,
58    "bn256_g2",
59    |domain_prefix| hash_to_curve_g2(domain_prefix),
60    crate::serde::CompressedFlagConfig::TwoSpare,
61    standard_sign
62);
63
64#[allow(clippy::type_complexity)]
65pub(crate) fn hash_to_curve_g2<'a>(domain_prefix: &'a str) -> Box<dyn Fn(&[u8]) -> G2 + 'a> {
66    let suite = G2::default_hash_to_curve_suite();
67    Box::new(move |message| {
68        let r0 = suite.hash_to_curve(domain_prefix, message);
69        r0.clear_cofactor()
70    })
71}
72
73const G1_GENERATOR_X: Fq = Fq::ONE;
74const G1_GENERATOR_Y: Fq = Fq::from_raw([2, 0, 0, 0]);
75const G1_A: Fq = Fq::ZERO;
76const G1_B: Fq = Fq::from_raw([3, 0, 0, 0]);
77
78const G2_A: Fq2 = Fq2 {
79    c0: Fq::ZERO,
80    c1: Fq::ZERO,
81};
82
83const G2_B: Fq2 = Fq2 {
84    c0: Fq::from_raw([
85        0x3267e6dc24a138e5,
86        0xb5b4c5e559dbefa3,
87        0x81be18991be06ac3,
88        0x2b149d40ceb8aaae,
89    ]),
90    c1: Fq::from_raw([
91        0xe4a2bd0685c315d2,
92        0xa74fa084e52d1852,
93        0xcd2cafadeed8fdf4,
94        0x009713b03af0fed4,
95    ]),
96};
97
98const G2_GENERATOR_X: Fq2 = Fq2 {
99    c0: Fq::from_raw([
100        0x46debd5cd992f6ed,
101        0x674322d4f75edadd,
102        0x426a00665e5c4479,
103        0x1800deef121f1e76,
104    ]),
105    c1: Fq::from_raw([
106        0x97e485b7aef312c2,
107        0xf1aa493335a9e712,
108        0x7260bfb731fb5d25,
109        0x198e9393920d483a,
110    ]),
111};
112
113const G2_GENERATOR_Y: Fq2 = Fq2 {
114    c0: Fq::from_raw([
115        0x4ce6cc0166fa7daa,
116        0xe3d1e7690c43d37b,
117        0x4aab71808dcb408f,
118        0x12c85ea5db8c6deb,
119    ]),
120
121    c1: Fq::from_raw([
122        0x55acdadcd122975b,
123        0xbc4b313370b38ef3,
124        0xec9e99ad690c3395,
125        0x090689d0585ff075,
126    ]),
127};
128
129// Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go
130// with `bn256::Fr::ZETA`
131// See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md
132// to have more details about the endomorphism.
133const ENDO_PARAMS_BN: EndoParameters = EndoParameters {
134    // round(b2/n)
135    gamma1: [0xd91d232ec7e0b3d7, 0x2, 0, 0],
136    // round(-b1/n)
137    gamma2: [0x5398fd0300ff6565, 0x4ccef014a773d2d2, 0x02, 0],
138    b1: [0x89d3256894d213e3, 0, 0, 0],
139    b2: [0x0be4e1541221250b, 0x6f4d8248eeb859fd, 0, 0],
140};
141
142endo!(G1, Fr, ENDO_PARAMS_BN);
143
144impl group::cofactor::CofactorGroup for G1 {
145    type Subgroup = G1;
146
147    fn clear_cofactor(&self) -> Self {
148        *self
149    }
150
151    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
152        CtOption::new(self, 1.into())
153    }
154
155    fn is_torsion_free(&self) -> Choice {
156        1.into()
157    }
158}
159
160fn exp_by_x(g2: &G2) -> G2 {
161    let x = super::BN_X;
162
163    (0..62).rev().fold(*g2, |mut acc, i| {
164        println!("{}", ((x >> i) & 1) == 1);
165
166        acc = acc.double();
167        (((x >> i) & 1) == 1).then(|| acc += g2);
168        acc
169    })
170}
171
172fn psi(mut g2: G2) -> G2 {
173    const U0: Fq = Fq::from_raw([
174        0x99e39557176f553d,
175        0xb78cc310c2c3330c,
176        0x4c0bec3cf559b143,
177        0x2fb347984f7911f7,
178    ]);
179
180    const U1: Fq = Fq::from_raw([
181        0x1665d51c640fcba2,
182        0x32ae2a1d0b7c9dce,
183        0x4ba4cc8bd75a0794,
184        0x16c9e55061ebae20,
185    ]);
186    let u = Fq2::new(U0, U1);
187
188    const V0: Fq = Fq::from_raw([
189        0xdc54014671a0135a,
190        0xdbaae0eda9c95998,
191        0xdc5ec698b6e2f9b9,
192        0x063cf305489af5dc,
193    ]);
194
195    const V1: Fq = Fq::from_raw([
196        0x82d37f632623b0e3,
197        0x21807dc98fa25bd2,
198        0x0704b5a7ec796f2b,
199        0x07c03cbcac41049a,
200    ]);
201    let v = Fq2::new(V0, V1);
202
203    g2.x.conjugate();
204    g2.y.conjugate();
205    g2.z.conjugate();
206
207    g2.x *= u;
208    g2.y *= v;
209
210    g2
211}
212
213impl CofactorGroup for G2 {
214    type Subgroup = G2;
215
216    fn clear_cofactor(&self) -> Self {
217        let u0 = exp_by_x(self);
218        let u1 = psi(u0.double() + u0);
219        let u2 = psi(psi(u0));
220        let u3 = psi(psi(psi(*self)));
221        u0 + u1 + u2 + u3
222    }
223
224    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
225        unimplemented!();
226    }
227
228    fn is_torsion_free(&self) -> Choice {
229        let u = exp_by_x(self);
230        let pu = psi(u);
231        let ppu = psi(pu);
232        let pppu = psi(ppu);
233        (ppu + pu + u + self - pppu.double()).is_identity()
234    }
235}
236
237impl G1 {
238    const SVDW_Z: Fq = Fq::ONE;
239
240    fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<Self, sha2::Sha256, 48> {
241        crate::hash_to_curve::Suite::<G1, sha2::Sha256, 48>::new(
242            b"BN254G1_XMD:SHA-256_SVDW_RO_",
243            Self::SVDW_Z,
244            crate::hash_to_curve::Method::SVDW,
245        )
246    }
247}
248
249impl G2 {
250    const SVDW_Z: Fq2 = Fq2::ONE;
251
252    fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<Self, sha2::Sha256, 96> {
253        crate::hash_to_curve::Suite::<G2, sha2::Sha256, 96>::new(
254            b"BN254G2_XMD:SHA-256_SVDW_RO_",
255            Self::SVDW_Z,
256            crate::hash_to_curve::Method::SVDW,
257        )
258    }
259}
260
261#[cfg(test)]
262mod test {
263    use group::UncompressedEncoding;
264    use rand_core::OsRng;
265
266    use super::*;
267    use crate::{serde::SerdeObject, tests::curve::TestH2C};
268
269    crate::curve_testing_suite!(G2, "clear_cofactor");
270    crate::curve_testing_suite!(G1, G2);
271    crate::curve_testing_suite!(G1, "endo_consistency");
272    crate::curve_testing_suite!(
273        G1,
274        "endo",
275        // Optional `z_other` param. `z_other` is 3-roots of unity, similar to `ZETA`.
276        // Reference: https://github.com/privacy-scaling-explorations/halo2curves/blob/main/src/bn256/fr.rs#L145-L151
277        [
278            0x8b17ea66b99c90dd,
279            0x5bfc41088d8daaa7,
280            0xb3c4d79d41a91758,
281            0x00,
282        ]
283    );
284
285    crate::curve_testing_suite!(
286        G1,
287        "constants",
288        Fq::MODULUS,
289        G1_A,
290        G1_B,
291        G1_GENERATOR_X,
292        G1_GENERATOR_Y,
293        Fr::MODULUS
294    );
295
296    crate::curve_testing_suite!(
297        G2,
298        "constants",
299        Fq2::MODULUS,
300        G2_A,
301        G2_B,
302        G2_GENERATOR_X,
303        G2_GENERATOR_Y,
304        Fr::MODULUS
305    );
306
307    #[test]
308    fn test_hash_to_curve_g1() {
309        // Test vectors are taken from gnark-crypto/ecc/bn254/hash_vectors_test.go
310        [
311            TestH2C::<G1Affine>::new(
312                b"",
313                crate::tests::point_from_hex(
314                    "0a976ab906170db1f9638d376514dbf8c42aef256a54bbd48521f20749e59e86",
315                    "02925ead66b9e68bfc309b014398640ab55f6619ab59bc1fab2210ad4c4d53d5",
316                ),
317            ),
318            TestH2C::<G1Affine>::new(
319                b"abc",
320                crate::tests::point_from_hex(
321                    "23f717bee89b1003957139f193e6be7da1df5f1374b26a4643b0378b5baf53d1",
322                    "04142f826b71ee574452dbc47e05bc3e1a647478403a7ba38b7b93948f4e151d",
323                ),
324            ),
325            TestH2C::<G1Affine>::new(
326                b"abcdef0123456789",
327                crate::tests::point_from_hex(
328                    "187dbf1c3c89aceceef254d6548d7163fdfa43084145f92c4c91c85c21442d4a",
329                    "0abd99d5b0000910b56058f9cc3b0ab0a22d47cf27615f588924fac1e5c63b4d",
330                ),
331            ),
332            TestH2C::<G1Affine>::new(
333                b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
334                crate::tests::point_from_hex(
335                    "00fe2b0743575324fc452d590d217390ad48e5a16cf051bee5c40a2eba233f5c",
336                    "0794211e0cc72d3cbbdf8e4e5cd6e7d7e78d101ff94862caae8acbe63e9fdc78",
337                ),
338            ),
339            TestH2C::<G1Affine>::new(
340                b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
341                crate::tests::point_from_hex(
342                    "01b05dc540bd79fd0fea4fbb07de08e94fc2e7bd171fe025c479dc212a2173ce",
343                    "1bf028afc00c0f843d113758968f580640541728cfc6d32ced9779aa613cd9b0",
344                ),
345            ),
346        ].iter().for_each(|test| {
347            test.run("QUUX-V01-CS02-with-");
348        });
349    }
350
351    #[test]
352    fn test_hash_to_curve_g2() {
353        pub(crate) fn point_from_hex(x0: &str, x1: &str, y0: &str, y1: &str) -> G2Affine {
354            let x0: Fq = crate::tests::hex_to_field(x0);
355            let x1: Fq = crate::tests::hex_to_field(x1);
356            let x = Fq2 { c0: x0, c1: x1 };
357            let y0: Fq = crate::tests::hex_to_field(y0);
358            let y1: Fq = crate::tests::hex_to_field(y1);
359            let y = Fq2 { c0: y0, c1: y1 };
360            G2Affine::from_xy(x, y).unwrap()
361        }
362
363        // Test vectors are taken from gnark-crypto/ecc/bn254/hash_vectors_test.go
364        [
365            TestH2C::<G2Affine>::new(
366                b"",
367                point_from_hex(
368                    "1192005a0f121921a6d5629946199e4b27ff8ee4d6dd4f9581dc550ade851300",
369                    "1747d950a6f23c16156e2171bce95d1189b04148ad12628869ed21c96a8c9335",
370                    "0498f6bb5ac309a07d9a8b88e6ff4b8de0d5f27a075830e1eb0e68ea318201d8",
371                    "2c9755350ca363ef2cf541005437221c5740086c2e909b71d075152484e845f4",
372                ),
373            ),
374            TestH2C::<G2Affine>::new(
375                b"abc",
376                point_from_hex(
377                    "16c88b54eec9af86a41569608cd0f60aab43464e52ce7e6e298bf584b94fccd2",
378                    "0b5db3ca7e8ef5edf3a33dfc3242357fbccead98099c3eb564b3d9d13cba4efd",
379                    "1c42ba524cb74db8e2c680449746c028f7bea923f245e69f89256af2d6c5f3ac",
380                    "22d02d2da7f288545ff8789e789902245ab08c6b1d253561eec789ec2c1bd630",
381                ),
382            ),
383            TestH2C::<G2Affine>::new(
384                b"abcdef0123456789",
385                point_from_hex(
386                    "1435fd84aa43c699230e371f6fea3545ce7e053cbbb06a320296a2b81efddc70",
387                    "2a8a360585b6b05996ef69c3c09b2c6fb17afe2b1e944f07559c53178eabf171",
388                    "2820188dcdc13ffdca31694942418afa1d6dfaaf259d012fab4da52b0f592e38",
389                    "142f08e2441ec431defc24621b73cfe0252d19b243cb55b84bdeb85de039207a",
390                ),
391            ),
392            TestH2C::<G2Affine>::new(
393                b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
394                point_from_hex(
395                    "2cffc213fb63d00d923cb22cda5a2904837bb93a2fe6e875c532c51744388341",
396                    "2718ef38d1bc4347f0266c774c8ef4ee5fa7056cc27a4bd7ecf7a888efb95b26",
397                    "232553f728341afa64ce66d00535764557a052e38657594e10074ad28728c584",
398                    "2206ec0a9288f31ed78531c37295df3b56c42a1284443ee9893adb1521779001",
399                ),
400            ),
401            TestH2C::<G2Affine>::new(
402                b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
403                point_from_hex(
404                    "242a0a159f36f87065e7c5170426012087023165ce47a486e53d6e2845ca625a",
405                    "17f9f6292998cf18ccc155903c1fe6b6465d40c794a3e1ed644a4182ad639f4a",
406                    "2dc5b7b65c9c79e6ef4afab8fbe3083c66d4ce31c78f6621ece17ecc892cf4b3",
407                    "18ef4886c818f01fdf309bc9a46dd904273917f85e74ecd0de62460a68122037",
408                ),
409            ),
410        ].iter().for_each(|test| {
411            test.run("QUUX-V01-CS02-with-");
412        });
413    }
414}