we (web engine): Experimental web browser project to understand the limits of Claude
1//! X25519 Diffie-Hellman key exchange (RFC 7748).
2//!
3//! Field arithmetic in GF(2^255 - 19) using 5×51-bit limb representation.
4//! Montgomery ladder scalar multiplication on Curve25519.
5//! Constant-time: no secret-dependent branches or memory accesses.
6
7// ---------------------------------------------------------------------------
8// GF(2^255 - 19) field element
9// ---------------------------------------------------------------------------
10
11const MASK51: u64 = (1u64 << 51) - 1;
12
13/// Basepoint u-coordinate = 9.
14const BASEPOINT: [u8; 32] = [
15 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16];
17
18#[derive(Clone, Copy)]
19struct Fe([u64; 5]);
20
21impl Fe {
22 const ZERO: Fe = Fe([0; 5]);
23 const ONE: Fe = Fe([1, 0, 0, 0, 0]);
24}
25
26// ---------------------------------------------------------------------------
27// Field encoding / decoding
28// ---------------------------------------------------------------------------
29
30fn fe_frombytes(bytes: &[u8; 32]) -> Fe {
31 let load8 = |src: &[u8]| -> u64 {
32 let mut dst = [0u8; 8];
33 let n = src.len().min(8);
34 dst[..n].copy_from_slice(&src[..n]);
35 u64::from_le_bytes(dst)
36 };
37 let mut h = Fe::ZERO;
38 h.0[0] = load8(&bytes[0..]) & MASK51;
39 h.0[1] = (load8(&bytes[6..]) >> 3) & MASK51;
40 h.0[2] = (load8(&bytes[12..]) >> 6) & MASK51;
41 h.0[3] = (load8(&bytes[19..]) >> 1) & MASK51;
42 h.0[4] = (load8(&bytes[24..]) >> 12) & MASK51;
43 h
44}
45
46fn fe_tobytes(h: &Fe) -> [u8; 32] {
47 let h = fe_reduce(h);
48 let mut out = [0u8; 32];
49 // Pack 5×51-bit limbs into 256 bits little-endian
50 let combine = |lo: u64, hi: u64, shift: u32| -> u64 { lo | (hi << shift) };
51 let w0 = combine(h.0[0], h.0[1], 51);
52 let w1 = combine(h.0[1] >> 13, h.0[2], 38);
53 let w2 = combine(h.0[2] >> 26, h.0[3], 25);
54 let w3 = combine(h.0[3] >> 39, h.0[4], 12);
55 out[0..8].copy_from_slice(&w0.to_le_bytes());
56 out[8..16].copy_from_slice(&w1.to_le_bytes());
57 out[16..24].copy_from_slice(&w2.to_le_bytes());
58 out[24..32].copy_from_slice(&w3.to_le_bytes());
59 out
60}
61
62// ---------------------------------------------------------------------------
63// Field addition and subtraction
64// ---------------------------------------------------------------------------
65
66fn fe_add(a: &Fe, b: &Fe) -> Fe {
67 Fe([
68 a.0[0] + b.0[0],
69 a.0[1] + b.0[1],
70 a.0[2] + b.0[2],
71 a.0[3] + b.0[3],
72 a.0[4] + b.0[4],
73 ])
74}
75
76fn fe_sub(a: &Fe, b: &Fe) -> Fe {
77 // Add 2*p before subtracting to avoid underflow.
78 // 2*p = 2*(2^255 - 19) in 5×51-bit limbs:
79 // limb 0: 2*(2^51 - 19) = 0xFFFFFFFFFFFDA
80 // limbs 1-4: 2*(2^51 - 1) = 0xFFFFFFFFFFFFE
81 Fe([
82 (a.0[0] + 0xFFFFFFFFFFFDA) - b.0[0],
83 (a.0[1] + 0xFFFFFFFFFFFFE) - b.0[1],
84 (a.0[2] + 0xFFFFFFFFFFFFE) - b.0[2],
85 (a.0[3] + 0xFFFFFFFFFFFFE) - b.0[3],
86 (a.0[4] + 0xFFFFFFFFFFFFE) - b.0[4],
87 ])
88}
89
90// ---------------------------------------------------------------------------
91// Field multiplication
92// ---------------------------------------------------------------------------
93
94fn fe_mul(a: &Fe, b: &Fe) -> Fe {
95 let a0 = a.0[0] as u128;
96 let a1 = a.0[1] as u128;
97 let a2 = a.0[2] as u128;
98 let a3 = a.0[3] as u128;
99 let a4 = a.0[4] as u128;
100
101 let b0 = b.0[0] as u128;
102 let b1 = b.0[1] as u128;
103 let b2 = b.0[2] as u128;
104 let b3 = b.0[3] as u128;
105 let b4 = b.0[4] as u128;
106
107 // Precompute 19*b[j] for reduction (2^255 ≡ 19 mod p)
108 let b1_19 = 19 * b1;
109 let b2_19 = 19 * b2;
110 let b3_19 = 19 * b3;
111 let b4_19 = 19 * b4;
112
113 // Schoolbook multiply with modular reduction
114 let mut t0 = a0 * b0 + a1 * b4_19 + a2 * b3_19 + a3 * b2_19 + a4 * b1_19;
115 let mut t1 = a0 * b1 + a1 * b0 + a2 * b4_19 + a3 * b3_19 + a4 * b2_19;
116 let mut t2 = a0 * b2 + a1 * b1 + a2 * b0 + a3 * b4_19 + a4 * b3_19;
117 let mut t3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0 + a4 * b4_19;
118 let mut t4 = a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
119
120 // Carry propagation
121 let c = t0 >> 51;
122 t0 &= MASK51 as u128;
123 t1 += c;
124
125 let c = t1 >> 51;
126 t1 &= MASK51 as u128;
127 t2 += c;
128
129 let c = t2 >> 51;
130 t2 &= MASK51 as u128;
131 t3 += c;
132
133 let c = t3 >> 51;
134 t3 &= MASK51 as u128;
135 t4 += c;
136
137 let c = t4 >> 51;
138 t4 &= MASK51 as u128;
139 t0 += c * 19;
140
141 let c = t0 >> 51;
142 t0 &= MASK51 as u128;
143 t1 += c;
144
145 Fe([t0 as u64, t1 as u64, t2 as u64, t3 as u64, t4 as u64])
146}
147
148fn fe_sq(a: &Fe) -> Fe {
149 let a0 = a.0[0] as u128;
150 let a1 = a.0[1] as u128;
151 let a2 = a.0[2] as u128;
152 let a3 = a.0[3] as u128;
153 let a4 = a.0[4] as u128;
154
155 let d0 = 2 * a0;
156 let d1 = 2 * a1;
157 let d2 = 2 * a2;
158 let d3 = 2 * a3;
159
160 let a4_19 = 19 * a4;
161 let a3_19 = 19 * a3;
162
163 let mut t0 = a0 * a0 + d1 * a4_19 + d2 * a3_19;
164 let mut t1 = d0 * a1 + d2 * a4_19 + a3 * a3_19;
165 let mut t2 = d0 * a2 + a1 * a1 + d3 * a4_19;
166 let mut t3 = d0 * a3 + d1 * a2 + a4 * a4_19;
167 let mut t4 = d0 * a4 + d1 * a3 + a2 * a2;
168
169 // Carry propagation
170 let c = t0 >> 51;
171 t0 &= MASK51 as u128;
172 t1 += c;
173
174 let c = t1 >> 51;
175 t1 &= MASK51 as u128;
176 t2 += c;
177
178 let c = t2 >> 51;
179 t2 &= MASK51 as u128;
180 t3 += c;
181
182 let c = t3 >> 51;
183 t3 &= MASK51 as u128;
184 t4 += c;
185
186 let c = t4 >> 51;
187 t4 &= MASK51 as u128;
188 t0 += c * 19;
189
190 let c = t0 >> 51;
191 t0 &= MASK51 as u128;
192 t1 += c;
193
194 Fe([t0 as u64, t1 as u64, t2 as u64, t3 as u64, t4 as u64])
195}
196
197/// Multiply by the curve constant a24 = (A - 2) / 4 = 121665 for Curve25519.
198/// This is the correct constant when the ladder uses AA (not BB) in the z_2 update.
199fn fe_mul_a24(a: &Fe) -> Fe {
200 let mut t0 = a.0[0] as u128 * 121665;
201 let mut t1 = a.0[1] as u128 * 121665;
202 let mut t2 = a.0[2] as u128 * 121665;
203 let mut t3 = a.0[3] as u128 * 121665;
204 let mut t4 = a.0[4] as u128 * 121665;
205
206 let c = t0 >> 51;
207 t0 &= MASK51 as u128;
208 t1 += c;
209
210 let c = t1 >> 51;
211 t1 &= MASK51 as u128;
212 t2 += c;
213
214 let c = t2 >> 51;
215 t2 &= MASK51 as u128;
216 t3 += c;
217
218 let c = t3 >> 51;
219 t3 &= MASK51 as u128;
220 t4 += c;
221
222 let c = t4 >> 51;
223 t4 &= MASK51 as u128;
224 t0 += c * 19;
225
226 let c = t0 >> 51;
227 t0 &= MASK51 as u128;
228 t1 += c;
229
230 Fe([t0 as u64, t1 as u64, t2 as u64, t3 as u64, t4 as u64])
231}
232
233// ---------------------------------------------------------------------------
234// Field reduction and canonical form
235// ---------------------------------------------------------------------------
236
237/// Fully reduce to canonical form in [0, p).
238fn fe_reduce(a: &Fe) -> Fe {
239 let mut h = *a;
240
241 // First, carry-propagate
242 let mut c = h.0[0] >> 51;
243 h.0[0] &= MASK51;
244 h.0[1] += c;
245
246 c = h.0[1] >> 51;
247 h.0[1] &= MASK51;
248 h.0[2] += c;
249
250 c = h.0[2] >> 51;
251 h.0[2] &= MASK51;
252 h.0[3] += c;
253
254 c = h.0[3] >> 51;
255 h.0[3] &= MASK51;
256 h.0[4] += c;
257
258 c = h.0[4] >> 51;
259 h.0[4] &= MASK51;
260 h.0[0] += c * 19;
261
262 c = h.0[0] >> 51;
263 h.0[0] &= MASK51;
264 h.0[1] += c;
265
266 // Now test if h >= p by computing h + 19 and checking overflow
267 let mut q = (h.0[0] + 19) >> 51;
268 q = (h.0[1] + q) >> 51;
269 q = (h.0[2] + q) >> 51;
270 q = (h.0[3] + q) >> 51;
271 q = (h.0[4] + q) >> 51;
272
273 // q is 1 if h >= p, 0 otherwise
274 h.0[0] += 19 * q;
275
276 // Carry-propagate again
277 c = h.0[0] >> 51;
278 h.0[0] &= MASK51;
279 h.0[1] += c;
280
281 c = h.0[1] >> 51;
282 h.0[1] &= MASK51;
283 h.0[2] += c;
284
285 c = h.0[2] >> 51;
286 h.0[2] &= MASK51;
287 h.0[3] += c;
288
289 c = h.0[3] >> 51;
290 h.0[3] &= MASK51;
291 h.0[4] += c;
292
293 h.0[4] &= MASK51;
294
295 h
296}
297
298// ---------------------------------------------------------------------------
299// Field inversion via Fermat's little theorem: a^(p-2)
300// ---------------------------------------------------------------------------
301
302/// Compute a^(2^n) by repeated squaring.
303fn fe_sq_n(a: &Fe, n: usize) -> Fe {
304 let mut r = fe_sq(a);
305 for _ in 1..n {
306 r = fe_sq(&r);
307 }
308 r
309}
310
311/// Compute a^(p-2) = a^(2^255 - 21) using an addition chain.
312fn fe_invert(a: &Fe) -> Fe {
313 let z1 = *a;
314
315 // t0 = a^2
316 let t0 = fe_sq(&z1);
317
318 // t1 = a^4
319 let t1 = fe_sq(&t0);
320
321 // t1 = a^8
322 let t1 = fe_sq(&t1);
323
324 // t1 = a^9 = a^(8+1)
325 let t1 = fe_mul(&t1, &z1);
326
327 // t0 = a^11 = a^(9+2)
328 let t0 = fe_mul(&t0, &t1);
329
330 // t2 = a^22
331 let t2 = fe_sq(&t0);
332
333 // t1 = a^31 = a^(22+9) = a^(2^5 - 1)
334 let t1 = fe_mul(&t1, &t2);
335
336 // t2 = a^(2^10 - 2^5)
337 let t2 = fe_sq_n(&t1, 5);
338
339 // t1 = a^(2^10 - 1)
340 let t1 = fe_mul(&t2, &t1);
341
342 // t2 = a^(2^20 - 2^10)
343 let t2 = fe_sq_n(&t1, 10);
344
345 // t2 = a^(2^20 - 1)
346 let t2 = fe_mul(&t2, &t1);
347
348 // t3 = a^(2^40 - 2^20)
349 let t3 = fe_sq_n(&t2, 20);
350
351 // t2 = a^(2^40 - 1)
352 let t2 = fe_mul(&t3, &t2);
353
354 // t2 = a^(2^50 - 2^10)
355 let t2 = fe_sq_n(&t2, 10);
356
357 // t1 = a^(2^50 - 1)
358 let t1 = fe_mul(&t2, &t1);
359
360 // t2 = a^(2^100 - 2^50)
361 let t2 = fe_sq_n(&t1, 50);
362
363 // t2 = a^(2^100 - 1)
364 let t2 = fe_mul(&t2, &t1);
365
366 // t3 = a^(2^200 - 2^100)
367 let t3 = fe_sq_n(&t2, 100);
368
369 // t2 = a^(2^200 - 1)
370 let t2 = fe_mul(&t3, &t2);
371
372 // t2 = a^(2^250 - 2^50)
373 let t2 = fe_sq_n(&t2, 50);
374
375 // t1 = a^(2^250 - 1)
376 let t1 = fe_mul(&t2, &t1);
377
378 // t1 = a^(2^255 - 2^5)
379 let t1 = fe_sq_n(&t1, 5);
380
381 // a^(2^255 - 21) = a^(p-2)
382 fe_mul(&t1, &t0)
383}
384
385// ---------------------------------------------------------------------------
386// Constant-time utilities
387// ---------------------------------------------------------------------------
388
389/// Constant-time conditional swap: swap a and b if swap == 1, no-op if swap == 0.
390fn fe_cswap(a: &mut Fe, b: &mut Fe, swap: u64) {
391 let mask = 0u64.wrapping_sub(swap);
392 for i in 0..5 {
393 let t = mask & (a.0[i] ^ b.0[i]);
394 a.0[i] ^= t;
395 b.0[i] ^= t;
396 }
397}
398
399// ---------------------------------------------------------------------------
400// Scalar clamping
401// ---------------------------------------------------------------------------
402
403fn clamp_scalar(s: &[u8; 32]) -> [u8; 32] {
404 let mut k = *s;
405 k[0] &= 248;
406 k[31] &= 127;
407 k[31] |= 64;
408 k
409}
410
411// ---------------------------------------------------------------------------
412// Montgomery ladder scalar multiplication
413// ---------------------------------------------------------------------------
414
415fn x25519_scalar_mult(scalar: &[u8; 32], u_point: &[u8; 32]) -> [u8; 32] {
416 let mut u_bytes = *u_point;
417 u_bytes[31] &= 127; // Mask bit 255 per RFC 7748
418 let u = fe_frombytes(&u_bytes);
419
420 let mut x_2 = Fe::ONE;
421 let mut z_2 = Fe::ZERO;
422 let mut x_3 = u;
423 let mut z_3 = Fe::ONE;
424 let mut swap: u64 = 0;
425
426 // Montgomery ladder: iterate from bit 254 down to 0
427 for t in (0..=254).rev() {
428 let k_t = ((scalar[t >> 3] >> (t & 7)) & 1) as u64;
429 swap ^= k_t;
430 fe_cswap(&mut x_2, &mut x_3, swap);
431 fe_cswap(&mut z_2, &mut z_3, swap);
432 swap = k_t;
433
434 let a = fe_add(&x_2, &z_2);
435 let aa = fe_sq(&a);
436 let b = fe_sub(&x_2, &z_2);
437 let bb = fe_sq(&b);
438 let e = fe_sub(&aa, &bb);
439 let c = fe_add(&x_3, &z_3);
440 let d = fe_sub(&x_3, &z_3);
441 let da = fe_mul(&d, &a);
442 let cb = fe_mul(&c, &b);
443
444 x_3 = fe_sq(&fe_add(&da, &cb));
445 z_3 = fe_mul(&u, &fe_sq(&fe_sub(&da, &cb)));
446 x_2 = fe_mul(&aa, &bb);
447 z_2 = fe_mul(&e, &fe_add(&aa, &fe_mul_a24(&e)));
448 }
449
450 fe_cswap(&mut x_2, &mut x_3, swap);
451 fe_cswap(&mut z_2, &mut z_3, swap);
452
453 let result = fe_mul(&x_2, &fe_invert(&z_2));
454 fe_tobytes(&result)
455}
456
457// ---------------------------------------------------------------------------
458// Public API
459// ---------------------------------------------------------------------------
460
461/// Compute X25519 Diffie-Hellman: scalar multiplication of `u_point` by `scalar`.
462///
463/// The scalar is clamped internally per RFC 7748.
464pub fn x25519(scalar: &[u8; 32], u_point: &[u8; 32]) -> [u8; 32] {
465 let k = clamp_scalar(scalar);
466 x25519_scalar_mult(&k, u_point)
467}
468
469/// Compute the X25519 public key from a private key (basepoint multiplication).
470pub fn x25519_base(scalar: &[u8; 32]) -> [u8; 32] {
471 x25519(scalar, &BASEPOINT)
472}
473
474// ---------------------------------------------------------------------------
475// Tests
476// ---------------------------------------------------------------------------
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 fn hex(bytes: &[u8]) -> String {
483 bytes.iter().map(|b| format!("{:02x}", b)).collect()
484 }
485
486 fn from_hex(s: &str) -> Vec<u8> {
487 (0..s.len())
488 .step_by(2)
489 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
490 .collect()
491 }
492
493 fn hex32(s: &str) -> [u8; 32] {
494 let v = from_hex(s);
495 let mut out = [0u8; 32];
496 out.copy_from_slice(&v);
497 out
498 }
499
500 // --- Field arithmetic tests ---
501
502 #[test]
503 fn fe_encode_decode_roundtrip() {
504 let bytes: [u8; 32] = [
505 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
506 25, 26, 27, 28, 29, 30, 31, 0,
507 ];
508 let fe = fe_frombytes(&bytes);
509 let out = fe_tobytes(&fe);
510 assert_eq!(bytes, out);
511 }
512
513 #[test]
514 fn fe_mul_identity() {
515 let bytes = hex32("0900000000000000000000000000000000000000000000000000000000000000");
516 let a = fe_frombytes(&bytes);
517 let result = fe_mul(&a, &Fe::ONE);
518 assert_eq!(fe_tobytes(&result), bytes);
519 }
520
521 #[test]
522 fn fe_mul_commutative() {
523 let a = fe_frombytes(&hex32(
524 "0900000000000000000000000000000000000000000000000000000000000000",
525 ));
526 let b = fe_frombytes(&hex32(
527 "0500000000000000000000000000000000000000000000000000000000000000",
528 ));
529 let ab = fe_tobytes(&fe_mul(&a, &b));
530 let ba = fe_tobytes(&fe_mul(&b, &a));
531 assert_eq!(ab, ba);
532 }
533
534 #[test]
535 fn fe_sq_matches_mul() {
536 let a = fe_frombytes(&hex32(
537 "0900000000000000000000000000000000000000000000000000000000000000",
538 ));
539 let sq = fe_tobytes(&fe_sq(&a));
540 let mul = fe_tobytes(&fe_mul(&a, &a));
541 assert_eq!(sq, mul);
542 }
543
544 #[test]
545 fn fe_invert_roundtrip() {
546 let a = fe_frombytes(&hex32(
547 "0900000000000000000000000000000000000000000000000000000000000000",
548 ));
549 let inv = fe_invert(&a);
550 let product = fe_mul(&a, &inv);
551 assert_eq!(
552 fe_tobytes(&product),
553 hex32("0100000000000000000000000000000000000000000000000000000000000000")
554 );
555 }
556
557 #[test]
558 fn fe_add_sub_roundtrip() {
559 let a = fe_frombytes(&hex32(
560 "0900000000000000000000000000000000000000000000000000000000000000",
561 ));
562 let b = fe_frombytes(&hex32(
563 "0500000000000000000000000000000000000000000000000000000000000000",
564 ));
565 let sum = fe_add(&a, &b);
566 let diff = fe_sub(&sum, &b);
567 assert_eq!(fe_tobytes(&diff), fe_tobytes(&a));
568 }
569
570 // --- RFC 7748 §6.1 test vectors ---
571
572 #[test]
573 fn rfc7748_section_6_1_alice_public_key() {
574 let alice_private =
575 hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
576 let expected = hex32("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
577 assert_eq!(x25519_base(&alice_private), expected);
578 }
579
580 #[test]
581 fn rfc7748_section_6_1_bob_public_key() {
582 let bob_private = hex32("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
583 let expected = hex32("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
584 assert_eq!(x25519_base(&bob_private), expected);
585 }
586
587 #[test]
588 fn rfc7748_section_6_1_shared_secret() {
589 let alice_private =
590 hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
591 let bob_public = hex32("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
592 let expected = hex32("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
593 assert_eq!(x25519(&alice_private, &bob_public), expected);
594 }
595
596 #[test]
597 fn rfc7748_section_6_1_shared_secret_both_sides() {
598 let alice_private =
599 hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
600 let bob_private = hex32("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
601 let alice_public = x25519_base(&alice_private);
602 let bob_public = x25519_base(&bob_private);
603 let shared_ab = x25519(&alice_private, &bob_public);
604 let shared_ba = x25519(&bob_private, &alice_public);
605 assert_eq!(shared_ab, shared_ba);
606 assert_eq!(
607 hex(&shared_ab),
608 "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
609 );
610 }
611
612 // --- RFC 7748 §5.2 iterated test vectors ---
613
614 #[test]
615 fn rfc7748_section_5_2_one_iteration() {
616 let mut k = hex32("0900000000000000000000000000000000000000000000000000000000000000");
617 let mut u = k;
618 let output = x25519(&k, &u);
619 u = k;
620 k = output;
621 assert_eq!(
622 hex(&k),
623 "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079"
624 );
625 let _ = u; // suppress unused warning
626 }
627
628 #[test]
629 fn rfc7748_section_5_2_1000_iterations() {
630 let mut k = hex32("0900000000000000000000000000000000000000000000000000000000000000");
631 let mut u = k;
632 for _ in 0..1000 {
633 let output = x25519(&k, &u);
634 u = k;
635 k = output;
636 }
637 assert_eq!(
638 hex(&k),
639 "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51"
640 );
641 }
642
643 #[test]
644 #[ignore] // Takes too long for regular test runs
645 fn rfc7748_section_5_2_1000000_iterations() {
646 let mut k = hex32("0900000000000000000000000000000000000000000000000000000000000000");
647 let mut u = k;
648 for _ in 0..1_000_000 {
649 let output = x25519(&k, &u);
650 u = k;
651 k = output;
652 }
653 assert_eq!(
654 hex(&k),
655 "7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424"
656 );
657 }
658
659 // --- Low-order point test ---
660
661 #[test]
662 fn x25519_zero_point_gives_zero() {
663 let scalar = hex32("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
664 let zero_point = [0u8; 32];
665 let result = x25519(&scalar, &zero_point);
666 assert_eq!(result, [0u8; 32]);
667 }
668}