Claude Reveals the Origins of the NIST ECDSA Curve Seeds

TL;DR: Anthropic researchers report that an internal Claude research model recovered the origins of the NIST elliptic-curve "seed" values in under half an hour, showing they were produced by the 1990s C library pseudorandom generator lrand48 initialized with ordinary Unix timestamps — not by a hash of a secret passphrase. The finding is not a cryptographic break; Anthropic says it makes deliberate backdoors in the ECDSA curves much less plausible.

What was announced

Anthropic published research showing how the seed parameters behind the NIST elliptic curve standards were generated. The seeds had been an open question in cryptography for roughly 25 years because they were supplied by the NSA with no explanation of how they were derived.

An Anthropic employee asked an internal research model whether it could determine how the seeds were generated, without providing the known clue about a humorous passphrase. According to the post, Claude found the origins of the NIST P-192, P-224, P-256, P-384 and P-521 seeds in under half an hour.

The answer: the seeds were not the output of any hash algorithm. They were output from the C library's lrand48 random-number generator, seeded with a Unix timestamp.

Anthropic frames the result explicitly as a historical curiosity rather than a security advisory:

Readers should take this as a fun closing chapter and the end of years of speculation. There is no action to be taken or things to do differently. Most researchers trusted the curves already.

Why the seed values mattered

ECDSA is the most widely used elliptic curve cryptography standard. NIST adopted it in 2000 as part of the FIPS 186-2 Digital Signature Standard, which lists standard elliptic curves of varying sizes and security levels.

For curves over prime and binary fields, the specifications published a "seed" parameter whose hash value is used to define the coefficient b of the curve. The provenance of those seeds was unknown. As one example, the NIST P-256 curve seed was published as:

SEED = C49D3608 86E70493 6A6678E1 139D26B7 819F7E90

No information was given about where that value came from. Because the NSA supplied the seeds without justification, researchers could not rule out that the parameters had been chosen to contain a deliberate weakness. Neal Koblitz and Alfred Menezes summarized the controversy in their paper "A Riddle Wrapped in an Enigma." The mystery also prompted an amateur investigation into the seed origins and a public bounty for anyone who could find them.

The leading explanation for years came from Jerry Solinas, a longtime NSA employee, who said in 2015 that he believed the seeds were generated from a hash of a humorous passphrase:

"I believe there was a counter rather than multiple hashing, but I don't know details. The message was along the lines of 'Give Bob and Jerry a raise' or 'Bob and Jerry rule' or something like that. It was Bob Reiter who actually wrote the code, and he doesn't remember the details either. Nor were we able to find archives from so long ago. If they exist, they are no doubt sitting on a hard drive near the Ark of the Covenant."

That explanation turned out to be wrong. Solinas died in 2023, and other colleagues from the period were not responsive, leaving the question open.

What the finding means for ECDSA security

Anthropic states plainly that this is not a cryptographic break, a new attack, or a weakness in the ECDSA curves. The company says the result instead increases confidence that the curves were chosen without backdoors or intentional weaknesses.

The reasoning given is that the seeds were generated in a very simple, reproducible way: a common pseudorandom number generator initialized from the system clock. Anyone can now reproduce the values. That leaves much less room for subversion than a deliberately constructed or unexplained parameter set would.

How Claude found the seeds

Searches based on the rumored passphrase had come up empty. Claude took a different approach: reasoning about how the software that generated the curves must have worked.

Claude hypothesized that the software used a pseudorandom number generator shipped with 1990s C libraries. Those generators are weak enough that their output can be recognized, and recognizing the output would reveal which generator was used and possibly allow recovery of its initialization state.

Claude wrote a scanner that slid along each published seed one bit at a time and tested whether adjacent chunks were consecutive outputs of a 1990s-vintage generator. The scan covered 34 combinations of generator and output slices.

Claude then noticed a pattern in the top bits of some seeds: they were all zero. Written as five 32-bit words, four of the ten seeds have a 0 in the top bit for all 20 words. That would happen by chance with probability 1/5810. The pattern is a signature of the linear congruential generator lrand48, which returns only the top 31 bits of a 48-bit state.

To recover the state, Claude took the first word, tried all 2¹⁷ values of the 17 hidden bits, and checked that the next four words followed. That worked for all four seeds under discussion.

The prime-curve seeds came from the same lrand48 generator but kept fewer bits of each output. Recovering the internal state of a truncated linear congruential generator is a textbook lattice problem, solved with the Lenstra–Lenstra–Lovász (LLL) basis reduction algorithm. Because Claude did not know which bits of the state the seeds might expose, it ran the solver over every window. Five NIST prime-curve seeds plus five more from the ANSI X9.62 standard all matched the same pattern: lrand48() >> 23. Each recovered 48-bit state regenerates all 160 bits of its seed, which does not happen by chance.

Because a linear congruential generator runs backwards as easily as forwards, Claude stepped each state back until it reached the form srand48 would have initialized, with the low 16 bits equal to 0x330E. That internal state form recurs once every 65,536 steps, so reaching one is not evidence on its own. However, for the ten prime-curve seeds, every srand48 initialization was a Unix timestamp representing a date between May 1997 and April 1998. The four binary-curve initializations read as February 1984; with the timestamps doubled, they map to April 17, 1998, two weeks after the P-224 run, suggesting the program may have halved the clock.

Claude's internal reasoning at the time, as quoted in the post:

This is astonishing: it solves the 25-year-old mystery. Hmm wait. Let me be skeptical: am I in some sort of simulated/test environment? … The seeds match my memory of actual values … Hmm, is it plausible in reality? As of my knowledge … the bounty was unsolved. … All top bits clear—20 bits, chance 1e-6. So indeed real-world. Wow, it's a real discovery.

Hmm wait. Let me be careful about the plausibility: is it suspicious that such an obvious thing wasn't discovered in 25 years? It's verified by direct computation on my side: 160-bit match from a 32-bit seed + small index: no chance coincidences (10 × 160 bits). It's real.

The generation algorithm

The recovered algorithm samples bytes from the RNG initialized by a Unix timestamp (t) for some number of iterations (n). Each iteration draws 20 bytes as a candidate seed: the first 20 bytes are candidate 1, the next 20 bytes are candidate 2, and so on. P-256, for example, required 1842 iterations to output the standardized seed.

The recovered timestamps and iteration counts are:

Curve Timestamp (UTC) Iterations
prime239v1 1997-05-15 15:49:03 2185
prime239v2 1997-05-15 15:49:03 2320
prime239v3 1997-05-15 15:49:03 3048
P-192 1997-05-18 14:45:34 139
prime192v3 1997-05-18 14:45:34 5228
prime192v2 1997-05-18 14:45:34 6013
P-521 1997-08-08 01:12:04 1136
P-256 1997-08-16 01:52:34 1842
P-384 1997-08-30 14:50:08 2688
P-224 1998-04-03 12:52:07 7026

For the binary curves, the timestamps are halved and five words are drawn per candidate:

Curve Timestamp (UTC) Iterations
B-233 1998-04-17 15:24:18 155
B-283 1998-04-17 15:27:44 32
B-409 1998-04-17 15:30:18 60
B-571 1998-04-17 15:32:08 73

Anthropic published CC0-licensed code to reproduce the seeds. The core routine is:

from datetime import datetime, timezone

def unix(when):
    return int(datetime.fromisoformat(when).replace(tzinfo=timezone.utc).timestamp())

def candidate(t, n, per, shift, width):
    x, out = (t << 16) | 0x330E, ""                      # the state srand48(t) sets
    for i in range(per * n):
        x = (0x5DEECE66D * x + 0xB) % 2**48              # one step of lrand48
        if i >= per * (n - 1):
            out += "%0*X" % (width, x >> shift)
    return out

Running it reproduces the published seeds, including P-256 → C49D360886E704936A6678E1139D26B7819F7E90 and P-192 → 3045AE6FC8422F64ED579528D38120EAE12196D5.

Claude also found the origins of some base points

Each curve also comes with a base point G, the fixed point that every ECDSA public key is a multiple of. The seed does not determine G, so its origin was a separate question.

Claude found where some of the base points came from. The base points of the six curves generated in May 1997 — P-192 and the five ANSI X9.62 example curves prime192v2, prime192v3 and prime239v1 through prime239v3 — come from the random number generator in FreeLIP, Arjen Lenstra's long-integer arithmetic package. Early versions of Victor Shoup's NTL library shipped the same code.

The recipe is simple:

  1. Seed the generator with a small integer.
  2. Throw away seven outputs.
  3. Generate candidate x-coordinates from 26-bit outputs reduced mod p.
  4. Repeat step 3 until x is on the curve.

The recovered generator seeds are 20000 for P-192; 20420, 20507 and 20509 for prime239v1 through prime239v3; and 20512 and 20517 for prime192v2 and prime192v3. Anthropic states that, as far as it knows, no NIST base point had been explained before.

The base points of P-224, P-256, P-384 and P-521 do not fit this recipe, and Anthropic was not able to explain how they were selected.

Open questions

Not every 20-byte candidate seed could generate a viable curve, and the researchers observed that some potentially viable curves were skipped. For P-256, the published seed is candidate 1842, but candidate 876 of the same run also gives a prime-order curve of the right size with parameters chosen the same way as on the published curve. The P-224 run passed over three such curves, and the two runs behind the ANSI X9.62 example curves passed over seven more. Anthropic does not know what criteria may have been used, or whether this was an unintentional artifact.

Because the number of skipped candidates is small, the post argues it is not plausible that weakened curves were selected — that would imply weakened curves are common and easy to find. It remains conceivable that the curves were selected to have a specific property by searching over timestamps fed to the random number generator, but the post notes you would only need to do that if you intended to disclose the timestamp as evidence the seed was innocuously generated, and nothing in the numbers shows how many runs there were.

One seed remains unrecovered: the fifth binary seed, B-163, fits nothing Claude has tried and is still unexplained. The origins of the base points for P-224, P-256, P-384 and P-521, and for the NIST binary curves, also remain unknown.

The bounty

The $36,864 offered for finding the seeds will be donated to the Internet Security Research Group.

Who conducted the work

The work was conducted at Anthropic by John Wood, Henry de Valence, Ava Howell, Zack Voase, Lucy Sweet, Amir Omidi, Paul Crowley, Iain McGinniss, Nicholas Carlini and Steve Weis. The post also recognizes Jerry Solinas posthumously, and thanks Filippo Valsorda for organizing the NIST Elliptic Curves Seeds Bounty.

The reproduction code on the page is dedicated to the public domain under CC0 1.0 Universal.

Sources