halo2curves_axiom/bls12_381/hash_to_curve/
map_g2.rs

1//! Implementation of hash-to-curve for the G2 group
2//!
3//! Source: <https://github.com/privacy-scaling-explorations/bls12_381>
4
5use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};
6
7use super::chain::chain_p2m9div16;
8use super::{HashToField, MapToCurve, Sgn0};
9use crate::bls12_381::generic_array::{
10    typenum::{U128, U64},
11    GenericArray,
12};
13use crate::bls12_381::{fp::Fp, fp2::Fp2, g2::G2Projective};
14use ff::Field;
15
16/// Coefficients of the 3-isogeny x map's numerator
17const ISO3_XNUM: [Fp2; 4] = [
18    Fp2 {
19        c0: Fp::from_raw_unchecked([
20            0x47f6_71c7_1ce0_5e62,
21            0x06dd_5707_1206_393e,
22            0x7c80_cd2a_f3fd_71a2,
23            0x0481_03ea_9e6c_d062,
24            0xc545_16ac_c8d0_37f6,
25            0x1380_8f55_0920_ea41,
26        ]),
27        c1: Fp::from_raw_unchecked([
28            0x47f6_71c7_1ce0_5e62,
29            0x06dd_5707_1206_393e,
30            0x7c80_cd2a_f3fd_71a2,
31            0x0481_03ea_9e6c_d062,
32            0xc545_16ac_c8d0_37f6,
33            0x1380_8f55_0920_ea41,
34        ]),
35    },
36    Fp2 {
37        c0: Fp::zero(),
38        c1: Fp::from_raw_unchecked([
39            0x5fe5_5555_554c_71d0,
40            0x873f_ffdd_236a_aaa3,
41            0x6a6b_4619_b26e_f918,
42            0x21c2_8884_0887_4945,
43            0x2836_cda7_028c_abc5,
44            0x0ac7_3310_a7fd_5abd,
45        ]),
46    },
47    Fp2 {
48        c0: Fp::from_raw_unchecked([
49            0x0a0c_5555_5559_71c3,
50            0xdb0c_0010_1f9e_aaae,
51            0xb1fb_2f94_1d79_7997,
52            0xd396_0742_ef41_6e1c,
53            0xb700_40e2_c205_56f4,
54            0x149d_7861_e581_393b,
55        ]),
56        c1: Fp::from_raw_unchecked([
57            0xaff2_aaaa_aaa6_38e8,
58            0x439f_ffee_91b5_5551,
59            0xb535_a30c_d937_7c8c,
60            0x90e1_4442_0443_a4a2,
61            0x941b_66d3_8146_55e2,
62            0x0563_9988_53fe_ad5e,
63        ]),
64    },
65    Fp2 {
66        c0: Fp::from_raw_unchecked([
67            0x40aa_c71c_71c7_25ed,
68            0x1909_5555_7a84_e38e,
69            0xd817_050a_8f41_abc3,
70            0xd864_85d4_c87f_6fb1,
71            0x696e_b479_f885_d059,
72            0x198e_1a74_3280_02d2,
73        ]),
74        c1: Fp::zero(),
75    },
76];
77
78/// Coefficients of the 3-isogeny x map's denominator
79const ISO3_XDEN: [Fp2; 3] = [
80    Fp2 {
81        c0: Fp::zero(),
82        c1: Fp::from_raw_unchecked([
83            0x1f3a_ffff_ff13_ab97,
84            0xf25b_fc61_1da3_ff3e,
85            0xca37_57cb_3819_b208,
86            0x3e64_2736_6f8c_ec18,
87            0x0397_7bc8_6095_b089,
88            0x04f6_9db1_3f39_a952,
89        ]),
90    },
91    Fp2 {
92        c0: Fp::from_raw_unchecked([
93            0x4476_0000_0027_552e,
94            0xdcb8_009a_4348_0020,
95            0x6f7e_e9ce_4a6e_8b59,
96            0xb103_30b7_c0a9_5bc6,
97            0x6140_b1fc_fb1e_54b7,
98            0x0381_be09_7f0b_b4e1,
99        ]),
100        c1: Fp::from_raw_unchecked([
101            0x7588_ffff_ffd8_557d,
102            0x41f3_ff64_6e0b_ffdf,
103            0xf7b1_e8d2_ac42_6aca,
104            0xb374_1acd_32db_b6f8,
105            0xe9da_f5b9_482d_581f,
106            0x167f_53e0_ba74_31b8,
107        ]),
108    },
109    Fp2::one(),
110];
111
112/// Coefficients of the 3-isogeny y map's numerator
113const ISO3_YNUM: [Fp2; 4] = [
114    Fp2 {
115        c0: Fp::from_raw_unchecked([
116            0x96d8_f684_bdfc_77be,
117            0xb530_e4f4_3b66_d0e2,
118            0x184a_88ff_3796_52fd,
119            0x57cb_23ec_fae8_04e1,
120            0x0fd2_e39e_ada3_eba9,
121            0x08c8_055e_31c5_d5c3,
122        ]),
123        c1: Fp::from_raw_unchecked([
124            0x96d8_f684_bdfc_77be,
125            0xb530_e4f4_3b66_d0e2,
126            0x184a_88ff_3796_52fd,
127            0x57cb_23ec_fae8_04e1,
128            0x0fd2_e39e_ada3_eba9,
129            0x08c8_055e_31c5_d5c3,
130        ]),
131    },
132    Fp2 {
133        c0: Fp::zero(),
134        c1: Fp::from_raw_unchecked([
135            0xbf0a_71c7_1c91_b406,
136            0x4d6d_55d2_8b76_38fd,
137            0x9d82_f98e_5f20_5aee,
138            0xa27a_a27b_1d1a_18d5,
139            0x02c3_b2b2_d293_8e86,
140            0x0c7d_1342_0b09_807f,
141        ]),
142    },
143    Fp2 {
144        c0: Fp::from_raw_unchecked([
145            0xd7f9_5555_5553_1c74,
146            0x21cf_fff7_48da_aaa8,
147            0x5a9a_d186_6c9b_be46,
148            0x4870_a221_0221_d251,
149            0x4a0d_b369_c0a3_2af1,
150            0x02b1_ccc4_29ff_56af,
151        ]),
152        c1: Fp::from_raw_unchecked([
153            0xe205_aaaa_aaac_8e37,
154            0xfcdc_0007_6879_5556,
155            0x0c96_011a_8a15_37dd,
156            0x1c06_a963_f163_406e,
157            0x010d_f44c_82a8_81e6,
158            0x174f_4526_0f80_8feb,
159        ]),
160    },
161    Fp2 {
162        c0: Fp::from_raw_unchecked([
163            0xa470_bda1_2f67_f35c,
164            0xc0fe_38e2_3327_b425,
165            0xc9d3_d0f2_c6f0_678d,
166            0x1c55_c993_5b5a_982e,
167            0x27f6_c0e2_f074_6764,
168            0x117c_5e6e_28aa_9054,
169        ]),
170        c1: Fp::zero(),
171    },
172];
173
174/// Coefficients of the 3-isogeny y map's denominator
175const ISO3_YDEN: [Fp2; 4] = [
176    Fp2 {
177        c0: Fp::from_raw_unchecked([
178            0x0162_ffff_fa76_5adf,
179            0x8f7b_ea48_0083_fb75,
180            0x561b_3c22_59e9_3611,
181            0x11e1_9fc1_a9c8_75d5,
182            0xca71_3efc_0036_7660,
183            0x03c6_a03d_41da_1151,
184        ]),
185        c1: Fp::from_raw_unchecked([
186            0x0162_ffff_fa76_5adf,
187            0x8f7b_ea48_0083_fb75,
188            0x561b_3c22_59e9_3611,
189            0x11e1_9fc1_a9c8_75d5,
190            0xca71_3efc_0036_7660,
191            0x03c6_a03d_41da_1151,
192        ]),
193    },
194    Fp2 {
195        c0: Fp::zero(),
196        c1: Fp::from_raw_unchecked([
197            0x5db0_ffff_fd3b_02c5,
198            0xd713_f523_58eb_fdba,
199            0x5ea6_0761_a84d_161a,
200            0xbb2c_75a3_4ea6_c44a,
201            0x0ac6_7359_21c1_119b,
202            0x0ee3_d913_bdac_fbf6,
203        ]),
204    },
205    Fp2 {
206        c0: Fp::from_raw_unchecked([
207            0x66b1_0000_003a_ffc5,
208            0xcb14_00e7_64ec_0030,
209            0xa73e_5eb5_6fa5_d106,
210            0x8984_c913_a0fe_09a9,
211            0x11e1_0afb_78ad_7f13,
212            0x0542_9d0e_3e91_8f52,
213        ]),
214        c1: Fp::from_raw_unchecked([
215            0x534d_ffff_ffc4_aae6,
216            0x5397_ff17_4c67_ffcf,
217            0xbff2_73eb_870b_251d,
218            0xdaf2_8271_5287_0915,
219            0x393a_9cba_ca9e_2dc3,
220            0x14be_74db_faee_5748,
221        ]),
222    },
223    Fp2::one(),
224];
225
226const SSWU_ELLP_A: Fp2 = Fp2 {
227    c0: Fp::zero(),
228    c1: Fp::from_raw_unchecked([
229        0xe53a_0000_0313_5242,
230        0x0108_0c0f_def8_0285,
231        0xe788_9edb_e340_f6bd,
232        0x0b51_3751_2631_0601,
233        0x02d6_9857_17c7_44ab,
234        0x1220_b4e9_79ea_5467,
235    ]),
236};
237
238const SSWU_ELLP_B: Fp2 = Fp2 {
239    c0: Fp::from_raw_unchecked([
240        0x22ea_0000_0cf8_9db2,
241        0x6ec8_32df_7138_0aa4,
242        0x6e1b_9440_3db5_a66e,
243        0x75bf_3c53_a794_73ba,
244        0x3dd3_a569_412c_0a34,
245        0x125c_db5e_74dc_4fd1,
246    ]),
247    c1: Fp::from_raw_unchecked([
248        0x22ea_0000_0cf8_9db2,
249        0x6ec8_32df_7138_0aa4,
250        0x6e1b_9440_3db5_a66e,
251        0x75bf_3c53_a794_73ba,
252        0x3dd3_a569_412c_0a34,
253        0x125c_db5e_74dc_4fd1,
254    ]),
255};
256
257const SSWU_XI: Fp2 = Fp2 {
258    c0: Fp::from_raw_unchecked([
259        0x87eb_ffff_fff9_555c,
260        0x656f_ffe5_da8f_fffa,
261        0x0fd0_7493_45d3_3ad2,
262        0xd951_e663_0665_76f4,
263        0xde29_1a3d_41e9_80d3,
264        0x0815_664c_7dfe_040d,
265    ]),
266    c1: Fp::from_raw_unchecked([
267        0x43f5_ffff_fffc_aaae,
268        0x32b7_fff2_ed47_fffd,
269        0x07e8_3a49_a2e9_9d69,
270        0xeca8_f331_8332_bb7a,
271        0xef14_8d1e_a0f4_c069,
272        0x040a_b326_3eff_0206,
273    ]),
274};
275
276const SSWU_ETAS: [Fp2; 4] = [
277    Fp2 {
278        c0: Fp::from_raw_unchecked([
279            0x05e5_1466_8ac7_36d2,
280            0x9089_b4d6_b84f_3ea5,
281            0x603c_384c_224a_8b32,
282            0xf325_7909_536a_fea6,
283            0x5c5c_dbab_ae65_6d81,
284            0x075b_fa08_63c9_87e9,
285        ]),
286        c1: Fp::from_raw_unchecked([
287            0x338d_9bfe_0808_7330,
288            0x7b8e_48b2_bd83_cefe,
289            0x530d_ad5d_306b_5be7,
290            0x5a4d_7e8e_6c40_8b6d,
291            0x6258_f7a6_232c_ab9b,
292            0x0b98_5811_cce1_4db5,
293        ]),
294    },
295    Fp2 {
296        c0: Fp::from_raw_unchecked([
297            0x8671_6401_f7f7_377b,
298            0xa31d_b74b_f3d0_3101,
299            0x1423_2543_c645_9a3c,
300            0x0a29_ccf6_8744_8752,
301            0xe8c2_b010_201f_013c,
302            0x0e68_b9d8_6c9e_98e4,
303        ]),
304        c1: Fp::from_raw_unchecked([
305            0x05e5_1466_8ac7_36d2,
306            0x9089_b4d6_b84f_3ea5,
307            0x603c_384c_224a_8b32,
308            0xf325_7909_536a_fea6,
309            0x5c5c_dbab_ae65_6d81,
310            0x075b_fa08_63c9_87e9,
311        ]),
312    },
313    Fp2 {
314        c0: Fp::from_raw_unchecked([
315            0x718f_dad2_4ee1_d90f,
316            0xa58c_025b_ed82_76af,
317            0x0c3a_1023_0ab7_976f,
318            0xf0c5_4df5_c8f2_75e1,
319            0x4ec2_478c_28ba_f465,
320            0x1129_373a_90c5_08e6,
321        ]),
322        c1: Fp::from_raw_unchecked([
323            0x019a_f5f9_80a3_680c,
324            0x4ed7_da0e_6606_3afa,
325            0x6003_5472_3b5d_9972,
326            0x8b2f_958b_20d0_9d72,
327            0x0474_938f_02d4_61db,
328            0x0dcf_8b9e_0684_ab1c,
329        ]),
330    },
331    Fp2 {
332        c0: Fp::from_raw_unchecked([
333            0xb864_0a06_7f5c_429f,
334            0xcfd4_25f0_4b4d_c505,
335            0x072d_7e2e_bb53_5cb1,
336            0xd947_b5f9_d2b4_754d,
337            0x46a7_1427_4077_4afb,
338            0x0c31_864c_32fb_3b7e,
339        ]),
340        c1: Fp::from_raw_unchecked([
341            0x718f_dad2_4ee1_d90f,
342            0xa58c_025b_ed82_76af,
343            0x0c3a_1023_0ab7_976f,
344            0xf0c5_4df5_c8f2_75e1,
345            0x4ec2_478c_28ba_f465,
346            0x1129_373a_90c5_08e6,
347        ]),
348    },
349];
350
351const SSWU_RV1: Fp2 = Fp2 {
352    c0: Fp::from_raw_unchecked([
353        0x7bcf_a7a2_5aa3_0fda,
354        0xdc17_dec1_2a92_7e7c,
355        0x2f08_8dd8_6b4e_bef1,
356        0xd1ca_2087_da74_d4a7,
357        0x2da2_5966_96ce_bc1d,
358        0x0e2b_7eed_bbfd_87d2,
359    ]),
360    c1: Fp::from_raw_unchecked([
361        0x7bcf_a7a2_5aa3_0fda,
362        0xdc17_dec1_2a92_7e7c,
363        0x2f08_8dd8_6b4e_bef1,
364        0xd1ca_2087_da74_d4a7,
365        0x2da2_5966_96ce_bc1d,
366        0x0e2b_7eed_bbfd_87d2,
367    ]),
368};
369
370impl HashToField for Fp2 {
371    // ceil(log2(p)) = 381, m = 2, k = 128.
372    type InputLength = U128;
373
374    fn from_okm(okm: &GenericArray<u8, U128>) -> Fp2 {
375        let c0 = <Fp as HashToField>::from_okm(GenericArray::<u8, U64>::from_slice(&okm[..64]));
376        let c1 = <Fp as HashToField>::from_okm(GenericArray::<u8, U64>::from_slice(&okm[64..]));
377        Fp2 { c0, c1 }
378    }
379}
380
381impl Sgn0 for Fp2 {
382    fn sgn0(&self) -> Choice {
383        let sign_0 = self.c0.sgn0();
384        let zero_0 = self.c0.is_zero();
385        let sign_1 = self.c1.sgn0();
386        sign_0 | (zero_0 & sign_1)
387    }
388}
389
390/// Maps from an [`Fp2]` element to a point on iso-G2.
391fn map_to_curve_simple_swu(u: &Fp2) -> G2Projective {
392    let usq = u.square();
393    let xi_usq = SSWU_XI * usq;
394    let xisq_u4 = xi_usq.square();
395    let nd_common = xisq_u4 + xi_usq; // XI^2 * u^4 + XI * u^2
396    let x_den = SSWU_ELLP_A * Fp2::conditional_select(&(-nd_common), &SSWU_XI, nd_common.is_zero());
397    let x0_num = SSWU_ELLP_B * (Fp2::one() + nd_common); // B * (1 + (XI^2 * u^4 + XI * u^2))
398
399    // compute g(x0(u))
400    let x_densq = x_den.square();
401    let gx_den = x_densq * x_den;
402    // x0_num^3 + A * x0_num * x_den^2 + B * x_den^3
403    let gx0_num = (x0_num.square() + SSWU_ELLP_A * x_densq) * x0_num + SSWU_ELLP_B * gx_den;
404
405    // compute g(x0(u)) ^ ((p^2 - 9) // 16)
406    let sqrt_candidate = {
407        let vsq = gx_den.square(); // v^2
408        let v_3 = vsq * gx_den; // v^3
409        let v_4 = vsq.square(); // v^4
410        let uv_7 = gx0_num * v_3 * v_4; // u v^7
411        let uv_15 = uv_7 * v_4.square(); // u v^15
412        uv_7 * chain_p2m9div16(&uv_15) // u v^7 (u v^15) ^ ((p^2 - 9) // 16)
413    };
414
415    // set y = sqrt_candidate * Fp2::one(), check candidate against other roots of unity
416    let mut y = sqrt_candidate;
417    // check Fp2(0, 1)
418    let tmp = Fp2 {
419        c0: -sqrt_candidate.c1,
420        c1: sqrt_candidate.c0,
421    };
422    y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num));
423    // check Fp2(RV1, RV1)
424    let tmp = sqrt_candidate * SSWU_RV1;
425    y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num));
426    // check Fp2(RV1, -RV1)
427    let tmp = Fp2 {
428        c0: tmp.c1,
429        c1: -tmp.c0,
430    };
431    y.conditional_assign(&tmp, (tmp.square() * gx_den).ct_eq(&gx0_num));
432
433    // compute g(x1(u)) = g(x0(u)) * XI^3 * u^6
434    let gx1_num = gx0_num * xi_usq * xisq_u4;
435    // compute g(x1(u)) * u^3
436    let sqrt_candidate = sqrt_candidate * usq * u;
437    let mut eta_found = Choice::from(0u8);
438    for eta in &SSWU_ETAS[..] {
439        let tmp = sqrt_candidate * eta;
440        let found = (tmp.square() * gx_den).ct_eq(&gx1_num);
441        y.conditional_assign(&tmp, found);
442        eta_found |= found;
443    }
444
445    let x_num = Fp2::conditional_select(&x0_num, &(x0_num * xi_usq), eta_found);
446    // ensure sign of y and sign of u agree
447    y.conditional_negate(u.sgn0() ^ y.sgn0());
448
449    G2Projective {
450        x: x_num,
451        y: y * x_den,
452        z: x_den,
453    }
454}
455
456/// Maps from an iso-G2 point to a G2 point.
457fn iso_map(u: &G2Projective) -> G2Projective {
458    const COEFFS: [&[Fp2]; 4] = [&ISO3_XNUM, &ISO3_XDEN, &ISO3_YNUM, &ISO3_YDEN];
459
460    // unpack input point
461    let G2Projective { x, y, z } = *u;
462
463    // xnum, xden, ynum, yden
464    let mut mapvals = [Fp2::zero(); 4];
465
466    // compute powers of z
467    let zsq = z.square();
468    let zpows = [z, zsq, zsq * z];
469
470    // compute map value by Horner's rule
471    for idx in 0..4 {
472        let coeff = COEFFS[idx];
473        let clast = coeff.len() - 1;
474        mapvals[idx] = coeff[clast];
475        for jdx in 0..clast {
476            mapvals[idx] = mapvals[idx] * x + zpows[jdx] * coeff[clast - 1 - jdx];
477        }
478    }
479
480    // x denominator is order 1 less than x numerator, so we need an extra factor of z
481    mapvals[1] *= z;
482
483    // multiply result of Y map by the y-coord, y / z
484    mapvals[2] *= y;
485    mapvals[3] *= z;
486
487    G2Projective {
488        x: mapvals[0] * mapvals[3], // xnum * yden,
489        y: mapvals[2] * mapvals[1], // ynum * xden,
490        z: mapvals[1] * mapvals[3], // xden * yden
491    }
492}
493
494impl MapToCurve for G2Projective {
495    type Field = Fp2;
496
497    fn map_to_curve(u: &Fp2) -> G2Projective {
498        let pt = map_to_curve_simple_swu(u);
499        iso_map(&pt)
500    }
501
502    fn clear_h(&self) -> Self {
503        self.clear_cofactor()
504    }
505}
506
507#[cfg(test)]
508fn check_g2_prime(pt: &G2Projective) -> bool {
509    // (X : Y : Z)==(X/Z, Y/Z) is on E': y^2 = x^3 + A * x + B.
510    // y^2 z = (x^3) + A (x z^2) + B z^3
511    let zsq = pt.z.square();
512    (pt.y.square() * pt.z)
513        == (pt.x.square() * pt.x + SSWU_ELLP_A * pt.x * zsq + SSWU_ELLP_B * zsq * pt.z)
514}
515
516#[test]
517fn test_osswu_semirandom() {
518    use rand_core::SeedableRng;
519    let mut rng = rand_xorshift::XorShiftRng::from_seed([
520        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
521        0xe5,
522    ]);
523    for _ in 0..32 {
524        let input = Fp2::random(&mut rng);
525        let p = map_to_curve_simple_swu(&input);
526        assert!(check_g2_prime(&p));
527
528        let p_iso = iso_map(&p);
529        assert!(bool::from(p_iso.is_on_curve()));
530    }
531}
532
533// test vectors from the draft 10 RFC
534#[test]
535fn test_encode_to_curve_10() {
536    use crate::bls12_381::{
537        g2::G2Affine,
538        hash_to_curve::{ExpandMsgXmd, HashToCurve},
539    };
540    use std::string::{String, ToString};
541
542    struct TestCase {
543        msg: &'static [u8],
544        expected: [&'static str; 4],
545    }
546    impl TestCase {
547        fn expected(&self) -> String {
548            self.expected[0].to_string() + self.expected[1] + self.expected[2] + self.expected[3]
549        }
550    }
551
552    const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_NU_";
553
554    let cases = vec![
555        TestCase {
556            msg: b"",
557            expected: [
558                "126b855e9e69b1f691f816e48ac6977664d24d99f8724868a184186469ddfd4617367e94527d4b74fc86413483afb35b",
559                "00e7f4568a82b4b7dc1f14c6aaa055edf51502319c723c4dc2688c7fe5944c213f510328082396515734b6612c4e7bb7",
560                "1498aadcf7ae2b345243e281ae076df6de84455d766ab6fcdaad71fab60abb2e8b980a440043cd305db09d283c895e3d",
561                "0caead0fd7b6176c01436833c79d305c78be307da5f6af6c133c47311def6ff1e0babf57a0fb5539fce7ee12407b0a42",
562            ],
563        },
564        TestCase {
565            msg: b"abc",
566            expected: [
567                "0296238ea82c6d4adb3c838ee3cb2346049c90b96d602d7bb1b469b905c9228be25c627bffee872def773d5b2a2eb57d",
568                "108ed59fd9fae381abfd1d6bce2fd2fa220990f0f837fa30e0f27914ed6e1454db0d1ee957b219f61da6ff8be0d6441f",
569                "153606c417e59fb331b7ae6bce4fbf7c5190c33ce9402b5ebe2b70e44fca614f3f1382a3625ed5493843d0b0a652fc3f",
570                "033f90f6057aadacae7963b0a0b379dd46750c1c94a6357c99b65f63b79e321ff50fe3053330911c56b6ceea08fee656",
571            ],
572        },
573        TestCase {
574            msg: b"abcdef0123456789",
575            expected: [
576                "0da75be60fb6aa0e9e3143e40c42796edf15685cafe0279afd2a67c3dff1c82341f17effd402e4f1af240ea90f4b659b",
577                "038af300ef34c7759a6caaa4e69363cafeed218a1f207e93b2c70d91a1263d375d6730bd6b6509dcac3ba5b567e85bf3",
578                "0492f4fed741b073e5a82580f7c663f9b79e036b70ab3e51162359cec4e77c78086fe879b65ca7a47d34374c8315ac5e",
579                "19b148cbdf163cf0894f29660d2e7bfb2b68e37d54cc83fd4e6e62c020eaa48709302ef8e746736c0e19342cc1ce3df4",
580            ]
581        },
582        TestCase {
583            msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
584                   qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
585                   qqqqqqqqqqqqqqqqqqqqqqqqq",
586            expected: [
587                "12c8c05c1d5fc7bfa847f4d7d81e294e66b9a78bc9953990c358945e1f042eedafce608b67fdd3ab0cb2e6e263b9b1ad",
588                "0c5ae723be00e6c3f0efe184fdc0702b64588fe77dda152ab13099a3bacd3876767fa7bbad6d6fd90b3642e902b208f9",
589                "11c624c56dbe154d759d021eec60fab3d8b852395a89de497e48504366feedd4662d023af447d66926a28076813dd646",
590                "04e77ddb3ede41b5ec4396b7421dd916efc68a358a0d7425bddd253547f2fb4830522358491827265dfc5bcc1928a569",
591            ]
592        },
593        TestCase {
594            msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
595                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
596                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
597                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
598                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
599                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
600                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
601                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
602                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
603                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
604            expected: [
605                "1565c2f625032d232f13121d3cfb476f45275c303a037faa255f9da62000c2c864ea881e2bcddd111edc4a3c0da3e88d",
606                "0ea4e7c33d43e17cc516a72f76437c4bf81d8f4eac69ac355d3bf9b71b8138d55dc10fd458be115afa798b55dac34be1",
607                "0f8991d2a1ad662e7b6f58ab787947f1fa607fce12dde171bc17903b012091b657e15333e11701edcf5b63ba2a561247",
608                "043b6f5fe4e52c839148dc66f2b3751e69a0f6ebb3d056d6465d50d4108543ecd956e10fa1640dfd9bc0030cc2558d28",
609            ]
610        }
611    ];
612
613    for case in cases {
614        let g = <G2Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::encode_to_curve(
615            case.msg, DOMAIN,
616        );
617        let g_uncompressed = G2Affine::from(g).to_uncompressed_be();
618
619        assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref()));
620    }
621}
622
623// test vectors from the draft 10 RFC
624#[test]
625fn test_hash_to_curve_10() {
626    use crate::bls12_381::{
627        g2::G2Affine,
628        hash_to_curve::{ExpandMsgXmd, HashToCurve},
629    };
630    use std::string::{String, ToString};
631
632    struct TestCase {
633        msg: &'static [u8],
634        expected: [&'static str; 4],
635    }
636    impl TestCase {
637        fn expected(&self) -> String {
638            self.expected[0].to_string() + self.expected[1] + self.expected[2] + self.expected[3]
639        }
640    }
641
642    const DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_";
643
644    let cases = vec![
645        TestCase {
646            msg: b"",
647            expected: [
648                "05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d",
649                "0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a",
650                "12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d6",
651                "0503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92",
652            ],
653        },
654        TestCase {
655            msg: b"abc",
656            expected: [
657                "139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd8",
658                "02c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e6",
659                "00aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd16",
660                "1787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48",
661            ],
662        },
663        TestCase {
664            msg: b"abcdef0123456789",
665            expected: [
666                "190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c",
667                "121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd0",
668                "0bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be",
669                "05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8",
670            ]
671        },
672        TestCase {
673            msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
674                   qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
675                   qqqqqqqqqqqqqqqqqqqqqqqqq",
676            expected: [
677                "0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb91",
678                "19a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da",
679                "09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e5662",
680                "14f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192",
681            ]
682        },
683        TestCase {
684            msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
685                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
686                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
687                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
688                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
689                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
690                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
691                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
692                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
693                   aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
694            expected: [
695                "11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d01569",
696                "01a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f62534",
697                "03a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab52",
698                "0b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e",
699            ]
700        }
701    ];
702
703    for case in cases {
704        let g = <G2Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(
705            case.msg, DOMAIN,
706        );
707        let g_uncompressed = G2Affine::from(g).to_uncompressed_be();
708
709        assert_eq!(case.expected(), hex::encode(g_uncompressed.as_ref()));
710    }
711}
712
713#[test]
714fn test_sgn0() {
715    use super::map_g1::P_M1_OVER2;
716
717    assert!(!bool::from(Fp2::zero().sgn0()));
718    assert!(bool::from(Fp2::one().sgn0()));
719    assert!(bool::from(
720        Fp2 {
721            c0: P_M1_OVER2,
722            c1: Fp::zero()
723        }
724        .sgn0()
725    ));
726    assert!(bool::from(
727        Fp2 {
728            c0: P_M1_OVER2,
729            c1: Fp::one()
730        }
731        .sgn0()
732    ));
733    assert!(bool::from(
734        Fp2 {
735            c0: Fp::zero(),
736            c1: P_M1_OVER2,
737        }
738        .sgn0()
739    ));
740    assert!(bool::from(
741        Fp2 {
742            c0: Fp::one(),
743            c1: P_M1_OVER2,
744        }
745        .sgn0()
746    ));
747
748    let p_p1_over2 = P_M1_OVER2 + Fp::one();
749    assert!(!bool::from(
750        Fp2 {
751            c0: p_p1_over2,
752            c1: Fp::zero()
753        }
754        .sgn0()
755    ));
756    assert!(!bool::from(
757        Fp2 {
758            c0: p_p1_over2,
759            c1: Fp::one()
760        }
761        .sgn0()
762    ));
763    assert!(!bool::from(
764        Fp2 {
765            c0: Fp::zero(),
766            c1: p_p1_over2,
767        }
768        .sgn0()
769    ));
770    assert!(bool::from(
771        Fp2 {
772            c0: Fp::one(),
773            c1: p_p1_over2,
774        }
775        .sgn0()
776    ));
777
778    assert!(bool::from(
779        Fp2 {
780            c0: P_M1_OVER2,
781            c1: -Fp::one()
782        }
783        .sgn0()
784    ));
785    assert!(!bool::from(
786        Fp2 {
787            c0: p_p1_over2,
788            c1: -Fp::one()
789        }
790        .sgn0()
791    ));
792    assert!(!bool::from(
793        Fp2 {
794            c0: Fp::zero(),
795            c1: -Fp::one()
796        }
797        .sgn0()
798    ));
799    assert!(bool::from(
800        Fp2 {
801            c0: P_M1_OVER2,
802            c1: p_p1_over2
803        }
804        .sgn0()
805    ));
806    assert!(!bool::from(
807        Fp2 {
808            c0: p_p1_over2,
809            c1: P_M1_OVER2
810        }
811        .sgn0()
812    ));
813
814    assert!(!bool::from(
815        Fp2 {
816            c0: -Fp::one(),
817            c1: P_M1_OVER2,
818        }
819        .sgn0()
820    ));
821    assert!(!bool::from(
822        Fp2 {
823            c0: -Fp::one(),
824            c1: p_p1_over2,
825        }
826        .sgn0()
827    ));
828    assert!(!bool::from(
829        Fp2 {
830            c0: -Fp::one(),
831            c1: Fp::zero(),
832        }
833        .sgn0()
834    ));
835    assert!(!bool::from(
836        Fp2 {
837            c0: p_p1_over2,
838            c1: P_M1_OVER2,
839        }
840        .sgn0()
841    ));
842    assert!(bool::from(
843        Fp2 {
844            c0: P_M1_OVER2,
845            c1: p_p1_over2,
846        }
847        .sgn0()
848    ));
849}