cert-machine · audit · a frontier lab’s claim, decided the day it was published

NIST’s ECDSA curve seeds: the timestamp-and-lrand48 origin, re-decided from the standards

On 16 October 2026 Anthropic published that the fourteen seeds behind the NIST and ANSI X9.62 elliptic curves — a question open since 1997 and carrying a bounty — are outputs of the C library’s lrand48, seeded from Unix timestamps of May 1997 to April 1998, and that six base points come from FreeLIP’s random generator. Every row of that claim that a computation can decide is re-decided here, the same evening, from the POSIX specification of drand48, from ANSI X9.62’s derivation as FIPS 186-2 prints its intermediates, and from FreeLIP’s source — by two programs that share no code with each other or with the claimant’s. All 34 rows hold. The claims beyond the checkable core are listed as readings, each with what would decide it.

The claim: anthropic.com/research/ecdsa-curve-origins as fetched 2026-10-16 (sha256 afb5fba0659f…), its two code blocks pinned as evidence and never run. The published objects: FIPS PUB 186-2 (7384ae9ddbf8…) — for each prime curve p, r, the seed s, c, b, G; for each binary curve the field polynomial, r, and the seed and b in the normal basis — and OpenSSL’s crypto/ec/ec_curve.c at a pinned commit for the five X9.62 example curves, whose standard is paywalled; the two sources must agree on every NIST field or the reader refuses. The specifications: IEEE Std 1003.1 for the drand48 family; FreeLIP 1.1’s lip.c (Lenstra–Leyland, 1997) for zrstarts, zrandom and zrandomb. Pre-registered in corpus/curveseeds/preregistration.json with one dated amendment before any row was decided. Decided: that each published seed is the stated candidate of the stated srand48 run (14 rows); that each seed yields its published curve by X9.62’s SHA-1 procedure (14); that each of six base points is the first point FreeLIP’s generator draws from the stated seed (6). Not decided: the skipped-candidate claim, which needs point counting; the probabilities and the interpretation; anything about the security of the curves.

tl;dr
  • The finding. 34 of 34 checkable rows hold. Each of the fourteen seeds is exactly the stated 20-byte candidate of the lrand48 stream that srand48 starts from the stated Unix time — the five NIST prime curves and five X9.62 example curves as the top byte of each output (lrand48() >> 23), the four binary curves as whole 31-bit words from a clock the claim reads as halved. Each seed then yields its published curve under ANSI X9.62’s procedure: for the five NIST prime curves the SHA-1 chain reproduces the intermediate c that FIPS 186-2 itself prints, and b²·c ≡ −27 (mod p) for all ten prime curves; for the four binary curves the chain is the published b in the normal basis, bit for bit. The six base points of May 1997 are the first x on the curve that FreeLIP’s generator — zrstarts(seed), state × 3¹²¹ mod 2¹⁰⁷ − 1, 26-bit limbs, seven draws discarded — produces from LIP seeds 20000 to 20517; each published Gy squares to the curve. What the post claims beyond what it checks is in §4: the skipped candidates (point counting), the 1/5810, the halved clock, and "less plausible", each named for what it is. A seed reproducing from a 32-bit srand48 argument and a small index is the computation; that the argument is the moment a program ran in 1997 is the reader’s inference, and the page does not make it for them.
  • The mechanism. Integers only. The LCG from the man page (X ← 5DEECE66D·X + B mod 2⁴⁸; srand48 puts the argument in the high 32 bits over 330E; lrand48 is the high 31 bits); SHA-1 from the standard library; FreeLIP’s generator as lip.c codes it, including its rejection step. Two deciders — Python (int, hashlib) and Node (BigInt, crypto) — written separately, calibrated before any row on FIPS’s own printed c for P-256 and printed normal-basis b for B-233, agree on 383 fields; one difference refuses the run. 12 forgeries must fail and do: a timestamp one second off, a candidate one off, the wrong shift, the clock not halved, mrand48 for lrand48, a flipped seed byte, the chain run backwards, the prime chain without X9.62’s leftmost-bit rule, a LIP seed one off, 30-bit limbs, no outputs skipped.
  • Check it. node tools/run-curveseeds-ledger.js (seconds) · node instruments/curveseeds/battery.js — 10 checks, 15 red controls · python3 -I instruments/curveseeds/decide.py decides every row with nothing but the standard library.
seeds
14 of 14
each the stated candidate of the stated srand48 run; ten prime-curve, four binary-curve
seed → curve
14 of 14
X9.62’s SHA-1 chain: FIPS’s printed c reproduced (5), b²c ≡ −27 (10), the normal-basis b (4)
base points
6 of 6
FreeLIP’s generator from the stated LIP seeds; first x on the curve after 2, 3, 2, 1, 5, 1 tries
deciders
2 · 0
383 fields compared, no shared code, no difference
red controls
15
forgeries that must fail; all fired
readings
6
claims beyond the checkable core, each with what would decide it
§1 · the seeds

Fourteen seeds, each the stated candidate of the stated run

curvesrand48 clock (UTC)srand48 argumentcandidatethe published seedsourceverdict
P-1921997-05-18 14:45:348639667341393045ae6f c8422f64 ed579528 d38120ea e12196d5FIPS 186-2CERTIFIED
P-2241998-04-03 12:52:078916079277026bd713447 99d5c7fc dc45b59f a3b9ab8f 6a948bc5FIPS 186-2CERTIFIED
P-2561997-08-16 01:52:348716963541842c49d3608 86e70493 6a6678e1 139d26b7 819f7e90FIPS 186-2CERTIFIED
P-3841997-08-30 14:50:088729526082688a335926a a319a27a 1d00896a 6773a482 7acdac73FIPS 186-2CERTIFIED
P-5211997-08-08 01:12:048710027241136d09e8800 291cb853 96cc6717 393284aa a0da64baFIPS 186-2CERTIFIED
prime192v21997-05-18 14:45:34863966734601331a92ee2 029fd10d 901b113e 990710f0 d21ac6b6OpenSSL ec_curve.c (X9.62)CERTIFIED
prime192v31997-05-18 14:45:348639667345228c4696844 35deb378 c4b65ca9 591e2a57 63059a2eOpenSSL ec_curve.c (X9.62)CERTIFIED
prime239v11997-05-15 15:49:038637113432185e43bb460 f0b80cc0 c0b07579 8e948060 f8321b7dOpenSSL ec_curve.c (X9.62)CERTIFIED
prime239v21997-05-15 15:49:038637113432320e8b40116 04095303 ca3b8099 982be09f cb9ae616OpenSSL ec_curve.c (X9.62)CERTIFIED
prime239v31997-05-15 15:49:0386371134330487d737416 8ffe3471 b60a8576 86a19475 d3bfa2ffOpenSSL ec_curve.c (X9.62)CERTIFIED
B-2331998-04-17 15:24:18446413329 (= ⌊t/2⌋, t = 892826658)15574d59ff0 7f6b413d 0ea14b34 4b20a2db 049b50c3FIPS 186-2 (normal basis)CERTIFIED
B-2831998-04-17 15:27:44446413432 (= ⌊t/2⌋, t = 892826864)3277e2b073 70eb0f83 2a6dd5b6 2dfc88cd 06bb84beFIPS 186-2 (normal basis)CERTIFIED
B-4091998-04-17 15:30:18446413509 (= ⌊t/2⌋, t = 892827018)604099b5a4 57f9d69f 79213d09 4c4bcd4d 4262210bFIPS 186-2 (normal basis)CERTIFIED
B-5711998-04-17 15:32:08446413564 (= ⌊t/2⌋, t = 892827128)732aa058f7 3a0e33ab 486b0f61 0410c53a 7f132310FIPS 186-2 (normal basis)CERTIFIED

A CERTIFIED row says: start the POSIX generator with srand48(argument), take the n-th block of twenty outputs (prime curves: the top byte of each, lrand48() >> 23) or of five outputs (binary curves: each whole 31-bit value as a 32-bit word), and the 160 bits are the published seed. The candidate index is 1-based, as the post defines it ("the first 20 bytes are candidate 1"). The published seeds are read from the pinned FIPS 186-2 text layer (the NIST curves; the binary seeds in the normal basis FIPS gives them in) and from OpenSSL’s table (the X9.62 example curves), never retyped. The four binary runs are 206 seconds apart on the same afternoon under the halved-clock reading; without the halving the srand48 arguments read as February 1984 and the red control shows no seed reproduces.

§2 · seed → curve

Each seed yields its published curve by X9.62’s procedure

curvefieldthe chain’s outputagainst the standardverdict
P-192GF(p), 192 bitsc = 3099d2bbbfcb2538…equals FIPS 186-2’s printed c; b²c + 27 ≡ 0 (mod p)CERTIFIED
P-224GF(p), 224 bitsc = 5b056c7e11dd68f4…equals FIPS 186-2’s printed c; b²c + 27 ≡ 0 (mod p)CERTIFIED
P-256GF(p), 256 bitsc = 7efba1662985be94…equals FIPS 186-2’s printed c; b²c + 27 ≡ 0 (mod p)CERTIFIED
P-384GF(p), 384 bitsc = 79d1e655f868f02f…equals FIPS 186-2’s printed c; b²c + 27 ≡ 0 (mod p)CERTIFIED
P-521GF(p), 521 bitsc = b48bfa5f420a3494…equals FIPS 186-2’s printed c; b²c + 27 ≡ 0 (mod p)CERTIFIED
prime192v2GF(p), 192 bitsc = 15038d1d2e1cafee…no c printed for this curve; b²c + 27 ≡ 0 (mod p) holdsCERTIFIED
prime192v3GF(p), 192 bitsc = 25191f95024d8395…no c printed for this curve; b²c + 27 ≡ 0 (mod p) holdsCERTIFIED
prime239v1GF(p), 239 bitsc = 28b85ec1ecc19efe…no c printed for this curve; b²c + 27 ≡ 0 (mod p) holdsCERTIFIED
prime239v2GF(p), 239 bitsc = 1df491e44e7ccaf4…no c printed for this curve; b²c + 27 ≡ 0 (mod p) holdsCERTIFIED
prime239v3GF(p), 239 bitsc = 3a4f9dc9a6cefd5f…no c printed for this curve; b²c + 27 ≡ 0 (mod p) holdsCERTIFIED
B-233GF(2^233), t^233 + t^74 + 1b (normal basis) = 1a003e0962d4f9a8…equals FIPS 186-2’s normal-basis b, bit for bitCERTIFIED
B-283GF(2^283), t^283 + t^12 + t^7 + t^5 + 1b (normal basis) = 157261b894739fb5…equals FIPS 186-2’s normal-basis b, bit for bitCERTIFIED
B-409GF(2^409), t^409 + t^87 + 1b (normal basis) = 124d0651c3d3772f…equals FIPS 186-2’s normal-basis b, bit for bitCERTIFIED
B-571GF(2^571), t^571 + t^10 + t^5 + t^2 + 1b (normal basis) = 3762d0d471160061…equals FIPS 186-2’s normal-basis b, bit for bitCERTIFIED

ANSI X9.62-1998 A.3.3 turns a seed into a curve: with t the field size in bits, s = ⌊(t − 1)/160⌋ and v = t − 160s, the chain is the rightmost v bits of SHA-1(seed) followed by SHA-1(seed + 1) … SHA-1(seed + s). In the prime case the leftmost bit of the first block is cleared and the result is c, with b any solution of b²c ≡ a³ (mod p), a = −3; FIPS 186-2 prints that c, and the calibration that regenerates it is what caught the leftmost-bit rule (amendment 1). In the binary case the chain is b itself, in the normal basis FIPS uses for s and b. This row is the standard’s own promise, not the post’s; it is decided so that one record closes the chain from a 32-bit clock value to the published curve. For the X9.62 example curves no c is printed anywhere open, so the congruence alone decides.

§3 · the base points

Six base points from FreeLIP’s generator

curveLIP seedtriesx producedGyverdict
P-192200002188da80eb03090f6… = the published Gxsquares to x³ − 3x + b; the odd root, the smallerCERTIFIED
prime239v1204203ffa963cdca8816cc… = the published Gxsquares to x³ − 3x + b; the even root, the largerCERTIFIED
prime239v220507238af09d987277051… = the published Gxsquares to x³ − 3x + b; the even root, the largerCERTIFIED
prime239v32050916768ae8e18bb92cf… = the published Gxsquares to x³ − 3x + b; the odd root, the smallerCERTIFIED
prime192v2205125eea2bae7e1497842… = the published Gxsquares to x³ − 3x + b; the odd root, the smallerCERTIFIED
prime192v32051717d29778100c65a1d… = the published Gxsquares to x³ − 3x + b; the even root, the smallerCERTIFIED

FreeLIP (lip.c): zrstarts(seed) sets the state to the seed and the multiplier to 3¹²¹ mod (2¹⁰⁷ − 1); zrandom(b) advances the state and returns it mod b, redrawing in the rare case the state falls in the top partial block; zrandomb(p) draws one limb more than p has, the first most significant, and reduces mod p. The limb is 26 bits (NBITS in the SINGLE_MUL build of lip.h; 30 bits fails the red control), seven single draws are discarded first (the post’s reading of the generating program; zero fails the red control), and the first x with x³ − 3x + b a quadratic residue is the base point’s abscissa. The post explains no other base point — P-224, P-256, P-384, P-521 and the binary curves stay open — and nothing here changes that.

§4 · beyond the checkable core

What the post claims beyond what this page can decide

The point-counting lane for R1 (every candidate below the published index, under a stated root convention for b, with Schoof–Elkies–Atkin in PARI/GP) is outside the standard library and not part of this certificate; if run, its counts will appear here, counted apart.

§5 · conventions

The conventions every verdict above rests on

One amendment to the pre-registration, dated before any row was decided. Amendment 1. the first attempt produced c = fefba166… where FIPS prints 7efba166…; for P-192, b099d2bb against 3099d2bb — the leftmost bit of the first SHA-1 block is cleared. That is ANSI X9.62-1998 A.3.3.1's own rule for the prime case (set the leftmost bit of W0 to 0), omitted from the claims text above; the binary case A.3.3.2 has no such step and B-233's normal-basis b reproduced without it. the prime derivation clears bit v−1 of the first block; a red control is added — the chain without the clearing must not reproduce c. Nothing else changes; the calibration against the standard's own printed intermediate is exactly what caught this.

§6 · limits

What is not claimed