halo2curves/pluto_eris/
curve.rs

1use core::{
2    cmp,
3    fmt::Debug,
4    iter::Sum,
5    ops::{Add, Mul, Neg, Sub},
6};
7
8use group::cofactor::CofactorGroup;
9use rand::RngCore;
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
11
12use super::{fp::Fp, fp2::Fp2, fq::Fq};
13use crate::{
14    ff::{Field, PrimeField, WithSmallOrderMulGroup},
15    group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding},
16    impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative,
17    impl_binops_multiplicative_mixed, new_curve_impl, Coordinates, CurveAffine, CurveExt,
18};
19
20const G1_GENERATOR_X: Fp = Fp::from_raw([
21    0x9ffffcd2ffffffff,
22    0xa2a7e8c30006b945,
23    0xe4a7a5fe8fadffd6,
24    0x443f9a5cda8a6c7b,
25    0xa803ca76f439266f,
26    0x0130e0000d7f70e4,
27    0x2400000000002400,
28]);
29const G1_GENERATOR_Y: Fp = Fp::from_raw([0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
30
31const PLUTO_A: Fp = Fp::ZERO;
32const PLUTO_B: Fp = Fp::from_raw([0x39, 0, 0, 0, 0, 0, 0]);
33
34const ERIS_GENERATOR_X: Fq = Fq::from_raw([
35    0x1ffffcd2ffffffff,
36    0x9ca7e85d60050af4,
37    0xe4a775fe8e177fd6,
38    0x443f9a5c7a8a6c7b,
39    0xa803ca76f439266f,
40    0x0130e0000d7f70e4,
41    0x2400000000002400,
42]);
43const ERIS_GENERATOR_Y: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
44
45const ERIS_A: Fq = Fq::ZERO;
46const ERIS_B: Fq = Fq::from_raw([0x39, 0, 0, 0, 0, 0, 0]);
47
48const G2_GENERATOR_X: Fp2 = Fp2 {
49    // 0x13576c81faf3a13fd815d0e9bd54b845ee935948b84498b27ca972bfb93722e223c9e276a4ebe7559cfc86dd865f07d64f2b5fe6556f9066
50    c0: Fp::from_raw([
51        0x4f2b5fe6556f9066,
52        0x9cfc86dd865f07d6,
53        0x23c9e276a4ebe755,
54        0x7ca972bfb93722e2,
55        0xee935948b84498b2,
56        0xd815d0e9bd54b845,
57        0x13576c81faf3a13f,
58    ]),
59
60    //0x142164cb875db0465e5092f9380f44f555243d011699b7393029f2d201554727aeb383298fdf5847b9b3dff01bbe8d63fe7c781a8fd7bf21
61    c1: Fp::from_raw([
62        0xfe7c781a8fd7bf21,
63        0xb9b3dff01bbe8d63,
64        0xaeb383298fdf5847,
65        0x3029f2d201554727,
66        0x55243d011699b739,
67        0x5e5092f9380f44f5,
68        0x142164cb875db046,
69    ]),
70};
71const G2_GENERATOR_Y: Fp2 = Fp2 {
72    //0x2239f7408ead478c58e88d4df1e7418c42fdbb92e64ba85aa4dc17d7dace3f32eb471c004db774bfe78574aca67b3898cd1b78ad106ab9fe
73    c0: Fp::from_raw([
74        0xcd1b78ad106ab9fe,
75        0xe78574aca67b3898,
76        0xeb471c004db774bf,
77        0xa4dc17d7dace3f32,
78        0x42fdbb92e64ba85a,
79        0x58e88d4df1e7418c,
80        0x2239f7408ead478c,
81    ]),
82
83    // 0x1260b04d51136590dbb53dfd7caf450aeca714555bbe4f079ca65d97eb28fc9fc697b4e10bbcd9e0539ef82a731fb88ed49e3c080e6d945d
84    c1: Fp::from_raw([
85        0xd49e3c080e6d945d,
86        0x539ef82a731fb88e,
87        0xc697b4e10bbcd9e0,
88        0x9ca65d97eb28fc9f,
89        0xeca714555bbe4f07,
90        0xdbb53dfd7caf450a,
91        0x1260b04d51136590,
92    ]),
93};
94
95const TRITON_A: Fp2 = Fp2::ZERO;
96
97// u + 3
98const TRITON_B: Fp2 = Fp2 {
99    c0: Fp::from_raw([0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
100    c1: Fp::ONE,
101};
102
103impl CofactorGroup for G1 {
104    type Subgroup = G1;
105
106    fn clear_cofactor(&self) -> Self {
107        *self
108    }
109
110    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
111        CtOption::new(self, 1.into())
112    }
113
114    fn is_torsion_free(&self) -> Choice {
115        1.into()
116    }
117}
118
119new_curve_impl!(
120    (pub),
121    G1,
122    G1Affine,
123    Fp,
124    Fq,
125    (G1_GENERATOR_X,G1_GENERATOR_Y),
126    PLUTO_A,
127    PLUTO_B,
128    "pluto",
129    |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, G1::default_hash_to_curve_suite()),
130    crate::serde::CompressedFlagConfig::TwoSpare,
131    standard_sign
132);
133
134impl group::cofactor::CofactorGroup for Eris {
135    type Subgroup = Eris;
136
137    fn clear_cofactor(&self) -> Self {
138        *self
139    }
140
141    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
142        CtOption::new(self, 1.into())
143    }
144
145    fn is_torsion_free(&self) -> Choice {
146        1.into()
147    }
148}
149
150impl G1 {
151    /// Constant Z for the Shallue-van de Woestijne map.
152    /// Computed using https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#svdw-z-code
153    const SVDW_Z: Fp = Fp::ONE;
154
155    fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<Self, sha2::Sha256, 72> {
156        crate::hash_to_curve::Suite::<Self, sha2::Sha256, 72>::new(
157            b"pluto_XMD:SHA-256_SVDW_RO_",
158            Self::SVDW_Z,
159            crate::hash_to_curve::Method::SVDW,
160        )
161    }
162}
163
164new_curve_impl!(
165    (pub),
166    Eris,
167    ErisAffine,
168    Fq,
169    Fp,
170    (ERIS_GENERATOR_X,ERIS_GENERATOR_Y),
171    ERIS_A,
172    ERIS_B,
173    "eris",
174    |domain_prefix| crate::hash_to_curve::hash_to_curve(domain_prefix, Eris::default_hash_to_curve_suite()),
175    crate::serde::CompressedFlagConfig::TwoSpare,
176    standard_sign
177);
178
179impl CofactorGroup for G2 {
180    type Subgroup = G2;
181
182    fn clear_cofactor(&self) -> Self {
183        // cofactor = 2*p - q
184        //0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5d3a8a6c7be4a7d5fe91447fd6a8a7e928a00867971ffffcd300000001
185        let e: [u8; 56] = [
186            0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x30, 0xe0, 0x00, 0x0d, 0x7f,
187            0x70, 0xe4, 0xa8, 0x03, 0xca, 0x76, 0xf4, 0x39, 0x26, 0x6f, 0x44, 0x3f, 0x9a, 0x5d,
188            0x3a, 0x8a, 0x6c, 0x7b, 0xe4, 0xa7, 0xd5, 0xfe, 0x91, 0x44, 0x7f, 0xd6, 0xa8, 0xa7,
189            0xe9, 0x28, 0xa0, 0x08, 0x67, 0x97, 0x1f, 0xff, 0xfc, 0xd3, 0x00, 0x00, 0x00, 0x01,
190        ];
191
192        // self * TRITON_COFACTOR
193        let mut acc = G2::identity();
194        for bit in e
195            .iter()
196            .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
197            .skip(1)
198        {
199            acc = acc.double();
200            acc = G2::conditional_select(&acc, &(acc + self), bit);
201        }
202        acc
203    }
204
205    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
206        // TODO: Handle the case where the point is already in the subgroup.
207        CtOption::new(self.clear_cofactor(), 1.into())
208    }
209
210    fn is_torsion_free(&self) -> Choice {
211        // group order = q
212        let e: [u8; 56] = [
213            0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x30, 0xe0, 0x00, 0x0d, 0x7f,
214            0x70, 0xe4, 0xa8, 0x03, 0xca, 0x76, 0xf4, 0x39, 0x26, 0x6f, 0x44, 0x3f, 0x9a, 0x5c,
215            0x7a, 0x8a, 0x6c, 0x7b, 0xe4, 0xa7, 0x75, 0xfe, 0x8e, 0x17, 0x7f, 0xd6, 0x9c, 0xa7,
216            0xe8, 0x5d, 0x60, 0x05, 0x0a, 0xf4, 0x1f, 0xff, 0xfc, 0xd3, 0x00, 0x00, 0x00, 0x01,
217        ];
218        // self * GROUP_ORDER;
219        let mut acc = G2::identity();
220        for bit in e
221            .iter()
222            .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
223            .skip(1)
224        {
225            acc = acc.double();
226            acc = G2::conditional_select(&acc, &(acc + self), bit);
227        }
228        acc.is_identity()
229    }
230}
231
232impl Eris {
233    /// Constant Z for the Shallue-van de Woestijne map.
234    /// Computed using https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#svdw-z-code
235    const SVDW_Z: Fq = Fq::ONE;
236
237    fn default_hash_to_curve_suite() -> crate::hash_to_curve::Suite<Self, sha2::Sha256, 72> {
238        crate::hash_to_curve::Suite::<Eris, sha2::Sha256, 72>::new(
239            b"eris_XMD:SHA-256_SVDW_RO_",
240            Self::SVDW_Z,
241            crate::hash_to_curve::Method::SVDW,
242        )
243    }
244}
245
246impl crate::serde::endian::EndianRepr for Fp2 {
247    const ENDIAN: crate::serde::endian::Endian = Fq::ENDIAN;
248
249    fn to_bytes(&self) -> Vec<u8> {
250        self.to_bytes().to_vec()
251    }
252
253    fn from_bytes(bytes: &[u8]) -> subtle::CtOption<Self> {
254        Fp2::from_bytes(bytes[..Fp2::SIZE].try_into().unwrap())
255    }
256}
257
258new_curve_impl!(
259    (pub),
260    G2,
261    G2Affine,
262    Fp2,
263    Fq,
264    (G2_GENERATOR_X,G2_GENERATOR_Y),
265    TRITON_A,
266    TRITON_B,
267    "triton",
268    |_| unimplemented!(),
269    crate::serde::CompressedFlagConfig::TwoSpare,
270    standard_sign
271);
272
273#[cfg(test)]
274mod test {
275    use group::UncompressedEncoding;
276    use rand_core::OsRng;
277
278    use super::*;
279    use crate::serde::SerdeObject;
280
281    crate::curve_testing_suite!(G2, "clear_cofactor");
282    crate::curve_testing_suite!(G1, Eris, G2);
283    crate::curve_testing_suite!(G1, Eris, "hash_to_curve");
284    crate::curve_testing_suite!(G1, Eris, "endo_consistency");
285    crate::curve_testing_suite!(
286        G1,
287        "constants",
288        Fp::MODULUS,
289        PLUTO_A,
290        PLUTO_B,
291        G1_GENERATOR_X,
292        G1_GENERATOR_Y,
293        Fq::MODULUS
294    );
295    crate::curve_testing_suite!(
296        Eris,
297        "constants",
298        Fq::MODULUS,
299        ERIS_A,
300        ERIS_B,
301        ERIS_GENERATOR_X,
302        ERIS_GENERATOR_Y,
303        Fp::MODULUS
304    );
305    crate::curve_testing_suite!(
306        G2,
307        "constants",
308        Fp2::MODULUS,
309        TRITON_A,
310        TRITON_B,
311        G2_GENERATOR_X,
312        G2_GENERATOR_Y,
313        Fq::MODULUS
314    );
315}