k256/arithmetic/
hash2curve.rs

1use elliptic_curve::bigint::{ArrayEncoding, U256};
2use elliptic_curve::consts::{U4, U48};
3use elliptic_curve::generic_array::GenericArray;
4use elliptic_curve::group::cofactor::CofactorGroup;
5use elliptic_curve::hash2curve::{
6    FromOkm, GroupDigest, Isogeny, IsogenyCoefficients, MapToCurve, OsswuMap, OsswuMapParams, Sgn0,
7};
8use elliptic_curve::subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
9use elliptic_curve::Field;
10
11use crate::{AffinePoint, ProjectivePoint, Scalar, Secp256k1};
12
13use super::FieldElement;
14
15impl GroupDigest for Secp256k1 {
16    type FieldElement = FieldElement;
17}
18
19impl FromOkm for FieldElement {
20    type Length = U48;
21
22    fn from_okm(data: &GenericArray<u8, Self::Length>) -> Self {
23        // 0x0000000000000001000000000000000000000000000000000000000000000000
24        const F_2_192: FieldElement = FieldElement::from_bytes_unchecked(&[
25            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
26            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27            0x00, 0x00, 0x00, 0x00,
28        ]);
29        let d0 = FieldElement::from_bytes_unchecked(&[
30            0, 0, 0, 0, 0, 0, 0, 0, data[0], data[1], data[2], data[3], data[4], data[5], data[6],
31            data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
32            data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
33        ]);
34        let d1 = FieldElement::from_bytes_unchecked(&[
35            0, 0, 0, 0, 0, 0, 0, 0, data[24], data[25], data[26], data[27], data[28], data[29],
36            data[30], data[31], data[32], data[33], data[34], data[35], data[36], data[37],
37            data[38], data[39], data[40], data[41], data[42], data[43], data[44], data[45],
38            data[46], data[47],
39        ]);
40        d0 * F_2_192 + d1
41    }
42}
43
44impl Sgn0 for FieldElement {
45    fn sgn0(&self) -> Choice {
46        self.normalize().is_odd()
47    }
48}
49
50impl OsswuMap for FieldElement {
51    const PARAMS: OsswuMapParams<Self> = OsswuMapParams {
52        // See section 8.7 in
53        // <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/>
54        c1: &[
55            0xffff_ffff_bfff_ff0b,
56            0xffff_ffff_ffff_ffff,
57            0xffff_ffff_ffff_ffff,
58            0x3fff_ffff_ffff_ffff,
59        ],
60        // 0x25e9711ae8c0dadc 0x46fdbcb72aadd8f4 0x250b65073012ec80 0xbc6ecb9c12973975
61        c2: FieldElement::from_bytes_unchecked(&[
62            0x25, 0xe9, 0x71, 0x1a, 0xe8, 0xc0, 0xda, 0xdc, 0x46, 0xfd, 0xbc, 0xb7, 0x2a, 0xad,
63            0xd8, 0xf4, 0x25, 0x0b, 0x65, 0x07, 0x30, 0x12, 0xec, 0x80, 0xbc, 0x6e, 0xcb, 0x9c,
64            0x12, 0x97, 0x39, 0x75,
65        ]),
66        // 0x3f8731abdd661adc 0xa08a5558f0f5d272 0xe953d363cb6f0e5d 0x405447c01a444533
67        map_a: FieldElement::from_bytes_unchecked(&[
68            0x3f, 0x87, 0x31, 0xab, 0xdd, 0x66, 0x1a, 0xdc, 0xa0, 0x8a, 0x55, 0x58, 0xf0, 0xf5,
69            0xd2, 0x72, 0xe9, 0x53, 0xd3, 0x63, 0xcb, 0x6f, 0x0e, 0x5d, 0x40, 0x54, 0x47, 0xc0,
70            0x1a, 0x44, 0x45, 0x33,
71        ]),
72        // 0x00000000000006eb
73        map_b: FieldElement::from_bytes_unchecked(&[
74            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76            0x00, 0x00, 0x06, 0xeb,
77        ]),
78        // 0xffffffffffffffff 0xffffffffffffffff 0xffffffffffffffff 0xfffffffefffffc24
79        z: FieldElement::from_bytes_unchecked(&[
80            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
81            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
82            0xff, 0xff, 0xfc, 0x24,
83        ]),
84    };
85
86    fn osswu(&self) -> (Self, Self) {
87        let tv1 = self.square(); // u^2
88        let tv3 = Self::PARAMS.z * tv1; // Z * u^2
89        let mut tv2 = tv3.square(); // tv3^2
90        let mut xd = tv2 + tv3; // tv3^2 + tv3
91        let x1n = Self::PARAMS.map_b * (xd + Self::ONE); // B * (xd + 1)
92        xd = (xd * Self::PARAMS.map_a.negate(1)).normalize(); // -A * xd
93
94        let tv = Self::PARAMS.z * Self::PARAMS.map_a;
95        xd.conditional_assign(&tv, xd.is_zero());
96
97        tv2 = xd.square(); //xd^2
98        let gxd = tv2 * xd; // xd^3
99        tv2 *= Self::PARAMS.map_a; // A * tv2
100
101        let mut gx1 = x1n * (tv2 + x1n.square()); //x1n *(tv2 + x1n^2)
102        tv2 = gxd * Self::PARAMS.map_b; // B * gxd
103        gx1 += tv2; // gx1 + tv2
104
105        let mut tv4 = gxd.square(); // gxd^2
106        tv2 = gx1 * gxd; // gx1 * gxd
107        tv4 *= tv2;
108
109        let y1 = tv4.pow_vartime(Self::PARAMS.c1) * tv2; // tv4^C1 * tv2
110        let x2n = tv3 * x1n; // tv3 * x1n
111
112        let y2 = y1 * Self::PARAMS.c2 * tv1 * self; // y1 * c2 * tv1 * u
113
114        tv2 = y1.square() * gxd; //y1^2 * gxd
115
116        let e2 = tv2.normalize().ct_eq(&gx1.normalize());
117
118        // if e2 , x = x1, else x = x2
119        let mut x = Self::conditional_select(&x2n, &x1n, e2);
120        // xn / xd
121        x *= xd.invert().unwrap();
122
123        // if e2, y = y1, else y = y2
124        let mut y = Self::conditional_select(&y2, &y1, e2);
125
126        y.conditional_assign(&-y, self.sgn0() ^ y.sgn0());
127        (x, y)
128    }
129}
130
131impl MapToCurve for FieldElement {
132    type Output = ProjectivePoint;
133
134    fn map_to_curve(&self) -> Self::Output {
135        let (rx, ry) = self.osswu();
136        let (qx, qy) = FieldElement::isogeny(rx, ry);
137
138        AffinePoint {
139            x: qx,
140            y: qy,
141            infinity: 0,
142        }
143        .into()
144    }
145}
146
147impl FromOkm for Scalar {
148    type Length = U48;
149
150    fn from_okm(data: &GenericArray<u8, Self::Length>) -> Self {
151        const F_2_192: Scalar = Scalar(U256::from_be_hex(
152            "0000000000000001000000000000000000000000000000000000000000000000",
153        ));
154
155        let mut d0 = GenericArray::default();
156        d0[8..].copy_from_slice(&data[0..24]);
157        let d0 = Scalar(U256::from_be_byte_array(d0));
158
159        let mut d1 = GenericArray::default();
160        d1[8..].copy_from_slice(&data[24..]);
161        let d1 = Scalar(U256::from_be_byte_array(d1));
162
163        d0 * F_2_192 + d1
164    }
165}
166
167impl Isogeny for FieldElement {
168    type Degree = U4;
169
170    // See section E.1 in
171    // <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/>
172    const COEFFICIENTS: IsogenyCoefficients<Self> = IsogenyCoefficients {
173        xnum: &[
174            FieldElement::from_bytes_unchecked(&[
175                0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38,
176                0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8d,
177                0xaa, 0xaa, 0xa8, 0xc7,
178            ]),
179            FieldElement::from_bytes_unchecked(&[
180                0x07, 0xd3, 0xd4, 0xc8, 0x0b, 0xc3, 0x21, 0xd5, 0xb9, 0xf3, 0x15, 0xce, 0xa7, 0xfd,
181                0x44, 0xc5, 0xd5, 0x95, 0xd2, 0xfc, 0x0b, 0xf6, 0x3b, 0x92, 0xdf, 0xff, 0x10, 0x44,
182                0xf1, 0x7c, 0x65, 0x81,
183            ]),
184            FieldElement::from_bytes_unchecked(&[
185                0x53, 0x4c, 0x32, 0x8d, 0x23, 0xf2, 0x34, 0xe6, 0xe2, 0xa4, 0x13, 0xde, 0xca, 0x25,
186                0xca, 0xec, 0xe4, 0x50, 0x61, 0x44, 0x03, 0x7c, 0x40, 0x31, 0x4e, 0xcb, 0xd0, 0xb5,
187                0x3d, 0x9d, 0xd2, 0x62,
188            ]),
189            FieldElement::from_bytes_unchecked(&[
190                0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38,
191                0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8e, 0x38, 0xe3, 0x8d,
192                0xaa, 0xaa, 0xa8, 0x8c,
193            ]),
194        ],
195        xden: &[
196            FieldElement::from_bytes_unchecked(&[
197                0xd3, 0x57, 0x71, 0x19, 0x3d, 0x94, 0x91, 0x8a, 0x9c, 0xa3, 0x4c, 0xcb, 0xb7, 0xb6,
198                0x40, 0xdd, 0x86, 0xcd, 0x40, 0x95, 0x42, 0xf8, 0x48, 0x7d, 0x9f, 0xe6, 0xb7, 0x45,
199                0x78, 0x1e, 0xb4, 0x9b,
200            ]),
201            FieldElement::from_bytes_unchecked(&[
202                0xed, 0xad, 0xc6, 0xf6, 0x43, 0x83, 0xdc, 0x1d, 0xf7, 0xc4, 0xb2, 0xd5, 0x1b, 0x54,
203                0x22, 0x54, 0x06, 0xd3, 0x6b, 0x64, 0x1f, 0x5e, 0x41, 0xbb, 0xc5, 0x2a, 0x56, 0x61,
204                0x2a, 0x8c, 0x6d, 0x14,
205            ]),
206            FieldElement::from_bytes_unchecked(&[
207                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209                0x00, 0x00, 0x00, 0x01,
210            ]),
211        ],
212        ynum: &[
213            FieldElement::from_bytes_unchecked(&[
214                0x4b, 0xda, 0x12, 0xf6, 0x84, 0xbd, 0xa1, 0x2f, 0x68, 0x4b, 0xda, 0x12, 0xf6, 0x84,
215                0xbd, 0xa1, 0x2f, 0x68, 0x4b, 0xda, 0x12, 0xf6, 0x84, 0xbd, 0xa1, 0x2f, 0x68, 0x4b,
216                0x8e, 0x38, 0xe2, 0x3c,
217            ]),
218            FieldElement::from_bytes_unchecked(&[
219                0xc7, 0x5e, 0x0c, 0x32, 0xd5, 0xcb, 0x7c, 0x0f, 0xa9, 0xd0, 0xa5, 0x4b, 0x12, 0xa0,
220                0xa6, 0xd5, 0x64, 0x7a, 0xb0, 0x46, 0xd6, 0x86, 0xda, 0x6f, 0xdf, 0xfc, 0x90, 0xfc,
221                0x20, 0x1d, 0x71, 0xa3,
222            ]),
223            FieldElement::from_bytes_unchecked(&[
224                0x29, 0xa6, 0x19, 0x46, 0x91, 0xf9, 0x1a, 0x73, 0x71, 0x52, 0x09, 0xef, 0x65, 0x12,
225                0xe5, 0x76, 0x72, 0x28, 0x30, 0xa2, 0x01, 0xbe, 0x20, 0x18, 0xa7, 0x65, 0xe8, 0x5a,
226                0x9e, 0xce, 0xe9, 0x31,
227            ]),
228            FieldElement::from_bytes_unchecked(&[
229                0x2f, 0x68, 0x4b, 0xda, 0x12, 0xf6, 0x84, 0xbd, 0xa1, 0x2f, 0x68, 0x4b, 0xda, 0x12,
230                0xf6, 0x84, 0xbd, 0xa1, 0x2f, 0x68, 0x4b, 0xda, 0x12, 0xf6, 0x84, 0xbd, 0xa1, 0x2f,
231                0x38, 0xe3, 0x8d, 0x84,
232            ]),
233        ],
234        yden: &[
235            FieldElement::from_bytes_unchecked(&[
236                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
237                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
238                0xff, 0xff, 0xf9, 0x3b,
239            ]),
240            FieldElement::from_bytes_unchecked(&[
241                0x7a, 0x06, 0x53, 0x4b, 0xb8, 0xbd, 0xb4, 0x9f, 0xd5, 0xe9, 0xe6, 0x63, 0x27, 0x22,
242                0xc2, 0x98, 0x94, 0x67, 0xc1, 0xbf, 0xc8, 0xe8, 0xd9, 0x78, 0xdf, 0xb4, 0x25, 0xd2,
243                0x68, 0x5c, 0x25, 0x73,
244            ]),
245            FieldElement::from_bytes_unchecked(&[
246                0x64, 0x84, 0xaa, 0x71, 0x65, 0x45, 0xca, 0x2c, 0xf3, 0xa7, 0x0c, 0x3f, 0xa8, 0xfe,
247                0x33, 0x7e, 0x0a, 0x3d, 0x21, 0x16, 0x2f, 0x0d, 0x62, 0x99, 0xa7, 0xbf, 0x81, 0x92,
248                0xbf, 0xd2, 0xa7, 0x6f,
249            ]),
250            FieldElement::from_bytes_unchecked(&[
251                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253                0x00, 0x00, 0x00, 0x01,
254            ]),
255        ],
256    };
257}
258
259impl CofactorGroup for ProjectivePoint {
260    type Subgroup = ProjectivePoint;
261
262    fn clear_cofactor(&self) -> Self::Subgroup {
263        *self
264    }
265
266    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
267        CtOption::new(self, 1.into())
268    }
269
270    fn is_torsion_free(&self) -> Choice {
271        1.into()
272    }
273}
274
275#[cfg(test)]
276mod tests {
277    use crate::{FieldElement, Scalar, Secp256k1, U256};
278    use elliptic_curve::{
279        bigint::{ArrayEncoding, NonZero, U384},
280        consts::U48,
281        generic_array::GenericArray,
282        group::cofactor::CofactorGroup,
283        hash2curve::{FromOkm, GroupDigest, MapToCurve},
284        Curve,
285    };
286    use hex_literal::hex;
287    use proptest::{num::u64::ANY, prelude::ProptestConfig, proptest};
288
289    #[test]
290    fn hash_to_curve() {
291        use elliptic_curve::hash2curve::{self, ExpandMsgXmd};
292        use hex_literal::hex;
293        use sha2::Sha256;
294
295        struct TestVector {
296            msg: &'static [u8],
297            p_x: [u8; 32],
298            p_y: [u8; 32],
299            u_0: [u8; 32],
300            u_1: [u8; 32],
301            q0_x: [u8; 32],
302            q0_y: [u8; 32],
303            q1_x: [u8; 32],
304            q1_y: [u8; 32],
305        }
306
307        const DST: &[u8] = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_";
308
309        const TEST_VECTORS: [TestVector; 5] = [
310            TestVector {
311                msg: b"",
312                p_x: hex!("c1cae290e291aee617ebaef1be6d73861479c48b841eaba9b7b5852ddfeb1346"),
313                p_y: hex!("64fa678e07ae116126f08b022a94af6de15985c996c3a91b64c406a960e51067"),
314                u_0: hex!("6b0f9910dd2ba71c78f2ee9f04d73b5f4c5f7fc773a701abea1e573cab002fb3"),
315                u_1: hex!("1ae6c212e08fe1a5937f6202f929a2cc8ef4ee5b9782db68b0d5799fd8f09e16"),
316                q0_x: hex!("74519ef88b32b425a095e4ebcc84d81b64e9e2c2675340a720bb1a1857b99f1e"),
317                q0_y: hex!("c174fa322ab7c192e11748beed45b508e9fdb1ce046dee9c2cd3a2a86b410936"),
318                q1_x: hex!("44548adb1b399263ded3510554d28b4bead34b8cf9a37b4bd0bd2ba4db87ae63"),
319                q1_y: hex!("96eb8e2faf05e368efe5957c6167001760233e6dd2487516b46ae725c4cce0c6"),
320            },
321            TestVector {
322                msg: b"abc",
323                p_x: hex!("3377e01eab42db296b512293120c6cee72b6ecf9f9205760bd9ff11fb3cb2c4b"),
324                p_y: hex!("7f95890f33efebd1044d382a01b1bee0900fb6116f94688d487c6c7b9c8371f6"),
325                u_0: hex!("128aab5d3679a1f7601e3bdf94ced1f43e491f544767e18a4873f397b08a2b61"),
326                u_1: hex!("5897b65da3b595a813d0fdcc75c895dc531be76a03518b044daaa0f2e4689e00"),
327                q0_x: hex!("07dd9432d426845fb19857d1b3a91722436604ccbbbadad8523b8fc38a5322d7"),
328                q0_y: hex!("604588ef5138cffe3277bbd590b8550bcbe0e523bbaf1bed4014a467122eb33f"),
329                q1_x: hex!("e9ef9794d15d4e77dde751e06c182782046b8dac05f8491eb88764fc65321f78"),
330                q1_y: hex!("cb07ce53670d5314bf236ee2c871455c562dd76314aa41f012919fe8e7f717b3"),
331            },
332            TestVector {
333                msg: b"abcdef0123456789",
334                p_x: hex!("bac54083f293f1fe08e4a70137260aa90783a5cb84d3f35848b324d0674b0e3a"),
335                p_y: hex!("4436476085d4c3c4508b60fcf4389c40176adce756b398bdee27bca19758d828"),
336                u_0: hex!("ea67a7c02f2cd5d8b87715c169d055a22520f74daeb080e6180958380e2f98b9"),
337                u_1: hex!("7434d0d1a500d38380d1f9615c021857ac8d546925f5f2355319d823a478da18"),
338                q0_x: hex!("576d43ab0260275adf11af990d130a5752704f79478628761720808862544b5d"),
339                q0_y: hex!("643c4a7fb68ae6cff55edd66b809087434bbaff0c07f3f9ec4d49bb3c16623c3"),
340                q1_x: hex!("f89d6d261a5e00fe5cf45e827b507643e67c2a947a20fd9ad71039f8b0e29ff8"),
341                q1_y: hex!("b33855e0cc34a9176ead91c6c3acb1aacb1ce936d563bc1cee1dcffc806caf57"),
342            },
343            TestVector {
344                msg: b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
345                p_x: hex!("e2167bc785333a37aa562f021f1e881defb853839babf52a7f72b102e41890e9"),
346                p_y: hex!("f2401dd95cc35867ffed4f367cd564763719fbc6a53e969fb8496a1e6685d873"),
347                u_0: hex!("eda89a5024fac0a8207a87e8cc4e85aa3bce10745d501a30deb87341b05bcdf5"),
348                u_1: hex!("dfe78cd116818fc2c16f3837fedbe2639fab012c407eac9dfe9245bf650ac51d"),
349                q0_x: hex!("9c91513ccfe9520c9c645588dff5f9b4e92eaf6ad4ab6f1cd720d192eb58247a"),
350                q0_y: hex!("c7371dcd0134412f221e386f8d68f49e7fa36f9037676e163d4a063fbf8a1fb8"),
351                q1_x: hex!("10fee3284d7be6bd5912503b972fc52bf4761f47141a0015f1c6ae36848d869b"),
352                q1_y: hex!("0b163d9b4bf21887364332be3eff3c870fa053cf508732900fc69a6eb0e1b672"),
353            },
354            TestVector {
355                msg: b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
356                p_x: hex!("e3c8d35aaaf0b9b647e88a0a0a7ee5d5bed5ad38238152e4e6fd8c1f8cb7c998"),
357                p_y: hex!("8446eeb6181bf12f56a9d24e262221cc2f0c4725c7e3803024b5888ee5823aa6"),
358                u_0: hex!("8d862e7e7e23d7843fe16d811d46d7e6480127a6b78838c277bca17df6900e9f"),
359                u_1: hex!("68071d2530f040f081ba818d3c7188a94c900586761e9115efa47ae9bd847938"),
360                q0_x: hex!("b32b0ab55977b936f1e93fdc68cec775e13245e161dbfe556bbb1f72799b4181"),
361                q0_y: hex!("2f5317098360b722f132d7156a94822641b615c91f8663be69169870a12af9e8"),
362                q1_x: hex!("148f98780f19388b9fa93e7dc567b5a673e5fca7079cd9cdafd71982ec4c5e12"),
363                q1_y: hex!("3989645d83a433bc0c001f3dac29af861f33a6fd1e04f4b36873f5bff497298a"),
364            },
365        ];
366
367        for test_vector in TEST_VECTORS {
368            // in parts
369            let mut u = [FieldElement::default(), FieldElement::default()];
370            hash2curve::hash_to_field::<ExpandMsgXmd<Sha256>, FieldElement>(
371                &[test_vector.msg],
372                &[DST],
373                &mut u,
374            )
375            .unwrap();
376            assert_eq!(u[0].to_bytes().as_slice(), test_vector.u_0);
377            assert_eq!(u[1].to_bytes().as_slice(), test_vector.u_1);
378
379            let q0 = u[0].map_to_curve();
380            let aq0 = q0.to_affine();
381            assert_eq!(aq0.x.to_bytes().as_slice(), test_vector.q0_x);
382            assert_eq!(aq0.y.to_bytes().as_slice(), test_vector.q0_y);
383
384            let q1 = u[1].map_to_curve();
385            let aq1 = q1.to_affine();
386            assert_eq!(aq1.x.to_bytes().as_slice(), test_vector.q1_x);
387            assert_eq!(aq1.y.to_bytes().as_slice(), test_vector.q1_y);
388
389            let p = q0.clear_cofactor() + q1.clear_cofactor();
390            let ap = p.to_affine();
391            assert_eq!(ap.x.to_bytes().as_slice(), test_vector.p_x);
392            assert_eq!(ap.y.to_bytes().as_slice(), test_vector.p_y);
393
394            // complete run
395            let pt = Secp256k1::hash_from_bytes::<ExpandMsgXmd<Sha256>>(&[test_vector.msg], &[DST])
396                .unwrap();
397            let apt = pt.to_affine();
398            assert_eq!(apt.x.to_bytes().as_slice(), test_vector.p_x);
399            assert_eq!(apt.y.to_bytes().as_slice(), test_vector.p_y);
400        }
401    }
402
403    #[test]
404    fn from_okm_fuzz() {
405        let mut wide_order = GenericArray::default();
406        wide_order[16..].copy_from_slice(&Secp256k1::ORDER.to_be_byte_array());
407        let wide_order = NonZero::new(U384::from_be_byte_array(wide_order)).unwrap();
408
409        let simple_from_okm = move |data: GenericArray<u8, U48>| -> Scalar {
410            let data = U384::from_be_slice(&data);
411
412            let scalar = data % wide_order;
413            let reduced_scalar = U256::from_be_slice(&scalar.to_be_byte_array()[16..]);
414
415            Scalar(reduced_scalar)
416        };
417
418        proptest!(ProptestConfig::with_cases(1000), |(b0 in ANY, b1 in ANY, b2 in ANY, b3 in ANY, b4 in ANY, b5 in ANY)| {
419            let mut data = GenericArray::default();
420            data[..8].copy_from_slice(&b0.to_be_bytes());
421            data[8..16].copy_from_slice(&b1.to_be_bytes());
422            data[16..24].copy_from_slice(&b2.to_be_bytes());
423            data[24..32].copy_from_slice(&b3.to_be_bytes());
424            data[32..40].copy_from_slice(&b4.to_be_bytes());
425            data[40..].copy_from_slice(&b5.to_be_bytes());
426
427            let from_okm = Scalar::from_okm(&data);
428            let simple_from_okm = simple_from_okm(data);
429            assert_eq!(from_okm, simple_from_okm);
430        });
431    }
432}