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 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 c1: &[
55 0xffff_ffff_bfff_ff0b,
56 0xffff_ffff_ffff_ffff,
57 0xffff_ffff_ffff_ffff,
58 0x3fff_ffff_ffff_ffff,
59 ],
60 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 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 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 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(); let tv3 = Self::PARAMS.z * tv1; let mut tv2 = tv3.square(); let mut xd = tv2 + tv3; let x1n = Self::PARAMS.map_b * (xd + Self::ONE); xd = (xd * Self::PARAMS.map_a.negate(1)).normalize(); let tv = Self::PARAMS.z * Self::PARAMS.map_a;
95 xd.conditional_assign(&tv, xd.is_zero());
96
97 tv2 = xd.square(); let gxd = tv2 * xd; tv2 *= Self::PARAMS.map_a; let mut gx1 = x1n * (tv2 + x1n.square()); tv2 = gxd * Self::PARAMS.map_b; gx1 += tv2; let mut tv4 = gxd.square(); tv2 = gx1 * gxd; tv4 *= tv2;
108
109 let y1 = tv4.pow_vartime(Self::PARAMS.c1) * tv2; let x2n = tv3 * x1n; let y2 = y1 * Self::PARAMS.c2 * tv1 * self; tv2 = y1.square() * gxd; let e2 = tv2.normalize().ct_eq(&gx1.normalize());
117
118 let mut x = Self::conditional_select(&x2n, &x1n, e2);
120 x *= xd.invert().unwrap();
122
123 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 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 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 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}