pasta_curves/
hashtocurve.rs
1use ff::{Field, FromUniformBytes, PrimeField};
5use static_assertions::const_assert;
6use subtle::ConstantTimeEq;
7
8use crate::arithmetic::CurveExt;
9
10pub fn hash_to_field<F: FromUniformBytes<64>>(
12 curve_id: &str,
13 domain_prefix: &str,
14 message: &[u8],
15 buf: &mut [F; 2],
16) {
17 assert!(domain_prefix.len() < 256);
18 assert!((22 + curve_id.len() + domain_prefix.len()) < 256);
19
20 const CHUNKLEN: usize = 64;
23 const_assert!(CHUNKLEN * 2 < 256);
24
25 const R_IN_BYTES: usize = 128;
27
28 let personal = [0u8; 16];
29 let empty_hasher = blake2b_simd::Params::new()
30 .hash_length(CHUNKLEN)
31 .personal(&personal)
32 .to_state();
33
34 let b_0 = empty_hasher
35 .clone()
36 .update(&[0; R_IN_BYTES])
37 .update(message)
38 .update(&[0, (CHUNKLEN * 2) as u8, 0])
39 .update(domain_prefix.as_bytes())
40 .update(b"-")
41 .update(curve_id.as_bytes())
42 .update(b"_XMD:BLAKE2b_SSWU_RO_")
43 .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
44 .finalize();
45
46 let b_1 = empty_hasher
47 .clone()
48 .update(b_0.as_array())
49 .update(&[1])
50 .update(domain_prefix.as_bytes())
51 .update(b"-")
52 .update(curve_id.as_bytes())
53 .update(b"_XMD:BLAKE2b_SSWU_RO_")
54 .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
55 .finalize();
56
57 let b_2 = {
58 let mut empty_hasher = empty_hasher;
59 for (l, r) in b_0.as_array().iter().zip(b_1.as_array().iter()) {
60 empty_hasher.update(&[*l ^ *r]);
61 }
62 empty_hasher
63 .update(&[2])
64 .update(domain_prefix.as_bytes())
65 .update(b"-")
66 .update(curve_id.as_bytes())
67 .update(b"_XMD:BLAKE2b_SSWU_RO_")
68 .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8])
69 .finalize()
70 };
71
72 for (big, buf) in [b_1, b_2].iter().zip(buf.iter_mut()) {
73 let mut little = [0u8; CHUNKLEN];
74 little.copy_from_slice(big.as_array());
75 little.reverse();
76 *buf = F::from_uniform_bytes(&little);
77 }
78}
79
80pub fn iso_map<F: Field, C: CurveExt<Base = F>, I: CurveExt<Base = F>>(
82 p: &I,
83 iso: &[C::Base; 13],
84) -> C {
85 let (x, y, z) = p.jacobian_coordinates();
89
90 let z2 = z.square();
91 let z3 = z2 * z;
92 let z4 = z2.square();
93 let z6 = z3.square();
94
95 let num_x = ((iso[0] * x + iso[1] * z2) * x + iso[2] * z4) * x + iso[3] * z6;
96 let div_x = (z2 * x + iso[4] * z4) * x + iso[5] * z6;
97
98 let num_y = (((iso[6] * x + iso[7] * z2) * x + iso[8] * z4) * x + iso[9] * z6) * y;
99 let div_y = (((x + iso[10] * z2) * x + iso[11] * z4) * x + iso[12] * z6) * z3;
100
101 let zo = div_x * div_y;
102 let xo = num_x * div_y * zo;
103 let yo = num_y * div_x * zo.square();
104
105 C::new_jacobian(xo, yo, zo).unwrap()
106}
107
108#[allow(clippy::many_single_char_names)]
109pub fn map_to_curve_simple_swu<F: PrimeField, C: CurveExt<Base = F>, I: CurveExt<Base = F>>(
110 u: &F,
111 theta: F,
112 z: F,
113) -> I {
114 let a = I::a();
137 let b = I::b();
138 let z_u2 = z * u.square();
139 let ta = z_u2.square() + z_u2;
140 let num_x1 = b * (ta + F::ONE);
141 let div = a * F::conditional_select(&-ta, &z, ta.is_zero());
142 let num2_x1 = num_x1.square();
143 let div2 = div.square();
144 let div3 = div2 * div;
145 let num_gx1 = (num2_x1 + a * div2) * num_x1 + b * div3;
146
147 let num_x2 = z_u2 * num_x1; let (gx1_square, y1) = F::sqrt_ratio(&num_gx1, &div3);
154
155 let y2 = theta * z_u2 * u * y1;
171 let num_x = F::conditional_select(&num_x2, &num_x1, gx1_square);
172 let y = F::conditional_select(&y2, &y1, gx1_square);
173
174 let y = F::conditional_select(&(-y), &y, u.is_odd().ct_eq(&y.is_odd()));
176
177 I::new_jacobian(num_x * div, y * div3, div).unwrap()
178}