we (web engine): Experimental web browser project to understand the limits of Claude
at float-layout 668 lines 19 kB view raw
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}