Five exact matches with a limited security meaning
Anthropic reports reproducing all five prime-field curve seeds from Federal Information Processing Standard 186-2. Anthropic and Filippo Valsorda say the discovery strengthens their confidence in the curves. That assessment does not prove every historical selection decision was benign. Sources: Anthropic, Filippo Valsorda, NIST
Anthropic published the timestamp and candidate inputs. The complete P-256 time is August 16, 1997, at 01:52:34 Coordinated Universal Time (UTC). The reported matches establish a reproducible route to the public seed bytes. They do not establish every historical selection decision or prove the absence of cryptographic backdoors. Sources: Anthropic
The reference values below are transcribed from the historical standard, rather than expected strings embedded in either published reproducer. The standard lists those seeds on printed pages 29–33. Its appendices separately describe deriving a curve from a seed and checking the relationship to the curve coefficient. Regenerating a seed therefore checks one link in a longer chain. The table below pairs each published input with its matching historical constant. Sources: Anthropic, NIST
Table
Five prime-field seed matches
| Curve | UTC input | One-based candidate | Matching hexadecimal seed |
|---|---|---|---|
| P-192 | May 18, 1997, 14:45:34 | 139 | 3045AE6FC8422F64ED579528D38120EAE12196D5 |
| P-224 | April 3, 1998, 12:52:07 | 7,026 | BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5 |
| P-256 | August 16, 1997, 01:52:34 | 1,842 | C49D360886E704936A6678E1139D26B7819F7E90 |
| P-384 | August 30, 1997, 14:50:08 | 2,688 | A335926AA319A27A1D00896A6773A4827ACDAC73 |
| P-521 | August 8, 1997, 01:12:04 | 1,136 | D09E8800291CB85396CC6717393284AAA0DA64BA |
Reproduce the bytes with explicit indexing
The generator follows the 48-bit recurrence in Anthropic’s Python reproducer and Valsorda’s Go verifier. It initializes from a timestamp, advances the state and extracts the upper byte. Twenty successive bytes form one candidate. Numbering starts at one, and initialization emits no byte. Taking the upper byte also agrees with shifting the 31-bit lrand48 output another 23 bits. Sources: Anthropic, GitHub
Save the implementation below as reproduce.mjs. Its BigInt arithmetic avoids intermediate rounding. Its timestamps and candidate indices come from Anthropic. Sources: Anthropic
The JavaScript below is an unverified adaptation of the published generation procedure. Its assertions compare generated seeds with the historical constants; they do not test cryptographic security.
import { readFileSync } from "node:fs";
import assert from "node:assert/strict";
function candidate(iso, k, per=20, shift=40, width=2, divisor=1) {
if (!Number.isInteger(k) || k < 1)
throw Error("one-based index required");
const t = BigInt(Math.floor(Date.parse(iso) / 1000 / divisor));
let x = (t << 16n) | 0x330en;
let out = "";
for (let j = 0; j < per * k; j++) {
x = (0x5deece66dn * x + 11n) & ((1n << 48n) - 1n);
if (j >= per * (k - 1))
out += (x >> BigInt(shift)).toString(16).padStart(width, "0");
}
return out.toUpperCase();
}
const cases = [
["P-192", "1997-05-18T14:45:34Z", 139],
["P-224", "1998-04-03T12:52:07Z", 7026],
["P-256", "1997-08-16T01:52:34Z", 1842],
["P-384", "1997-08-30T14:50:08Z", 2688],
["P-521", "1997-08-08T01:12:04Z", 1136],
];
// Independent reference file transcribed from FIPS, not either verifier.
const reference = new Map(
readFileSync(process.argv[2], "utf8").trim().split(/\r?\n/)
.map(line => {
const [name, ...words] = line.trim().split(/\s+/);
return [name, words.join("").toUpperCase()];
})
);
for (const [name, iso, k] of cases) {
const expected = reference.get(name);
assert.match(expected ?? "", /^[0-9A-F]{40}$/);
assert.equal(candidate(iso, k), expected);
}Save the reference values below separately as fips-seeds.txt; they reproduce the historical standard’s seed constants. Keeping the reference input separate makes the comparison easier to inspect. The commands that follow record the local Node version, run the assertions and calculate checksums for a separately logged execution. Sources: NIST
P-192 3045ae6f c8422f64 ed579528 d38120ea e12196d5
P-224 bd713447 99d5c7fc dc45b59f a3b9ab8f 6a948bc5
P-256 c49d3608 86e70493 6a6678e1 139d26b7 819f7e90
P-384 a335926a a319a27a 1d00896a 6773a482 7acdac73
P-521 d09e8800 291cb853 96cc6717 393284aa a0da64banode --version
node reproduce.mjs fips-seeds.txt
sha256sum reproduce.mjs fips-seeds.txtSkipped candidates leave selection questions
The published implementations answer different questions. Anthropic’s Python selects the final 20 bytes after advancing through the requested candidate. Valsorda’s Go program also derives curves and checks orders, using cached results or a computer algebra system. Its search stops after finding all embedded published seeds. Sources: Anthropic, GitHub
Valsorda reports four earlier acceptable candidates: P-256 candidate 876 and P-224 candidates 777, 1,485 and 5,736. Random-number-generator consumption during validation and implementation bugs are proposed explanations for these skipped candidates, not established causes. A fuller audit should record each seed, derived coefficient, chosen square root, order and rejection reason. Sources: Filippo Valsorda
Requiring the order’s bit length to fit within the field modulus’s bit length differs from requiring a smaller order. Valsorda identifies prime239v2 as having an order above its field modulus but below two to the power of 239. The Go verifier also uses an embedding-degree threshold stricter than the historical X9.62 threshold described in its comments. Acceptance rules therefore need examination alongside the generator. Sources: Filippo Valsorda, GitHub
A conditional selection bound
Valsorda’s selection argument considers every second in 1997–98 and the first 16 prime-order candidates per initialization. The table below gives the article’s arithmetic for those assumptions. It also includes an editorial hypothetical with a wider initialization range. Sources: Filippo Valsorda
This counts choices within a fixed generation setup. It measures neither P-256’s security level nor attack cost. The stopping allowance concerns acceptable candidates, so P-224’s selection at raw candidate 7,026 does not contradict it. Sources: Anthropic, Filippo Valsorda
Table
Assumptions behind the selection-freedom count
| Assumption | Calculated count | Interpretation |
|---|---|---|
| Every second in 1997–98 | 63,072,000 timestamp choices | A wider range changes the count |
| First 16 prime-order candidates | 1,009,152,000 combined choices | Acceptable candidates, not raw blocks |
| Fixed setup with those bounds | Approximately 29.9 selection bits | Does not measure attack cost |
| Editorial hypothetical: every 32-bit initialization value | 36 bits with 16 candidate choices | Alternative assumption, not historical evidence |
Unknown run counts add no choices beyond an exhaustively counted timestamp range with a fixed stopping rule. They matter because matching an output does not establish that historical operators respected those bounds. Counting trials also requires an estimate of the targeted weakness’s frequency before it can bound the chance of finding one. Valsorda addresses a specific hypothetical weak-curve selection technique; his argument should not become a claim covering every cryptographic backdoor. Sources: Filippo Valsorda
Base points and binary seeds leave provenance gaps
Anthropic reports a FreeLIP-generator recipe reproducing the P-192 base-point x-coordinate and five example x-coordinates from an American National Standards Institute standard. Its displayed verifier compares those coordinates, which alone does not document the choice between two possible y-coordinates. The other four federal prime-curve base points and the binary-curve base points remain unexplained in Anthropic’s account. Sources: Anthropic
Anthropic’s tentative binary-seed interpretation uses half the proposed Unix timestamp and five 31-bit outputs, each encoded as an eight-digit hexadecimal word. Anthropic displays outputs for B-233, B-283, B-409 and B-571. The proposed recipe does not explain why a historical program would halve a clock value. B-163’s seed also remains unexplained. Sources: Anthropic
Valsorda organized the bounty, placing his analysis within that process. An outside assessment should examine whether the timestamp window and stopping allowance adequately bound selection freedom. It should also consider whether skipped candidates suggest additional freedom. Unexplained base points deserve separate scrutiny from reproduced curve seeds. Sources: Filippo Valsorda
Audit provenance while retaining algorithm policy
These results reveal no new operational exploit and establish no immediate patch, key-rotation or migration requirement. Recovering a public parameter’s generation recipe is distinct from recovering a secret signing key. Developers can examine the provenance evidence without treating it as a substitute for implementation review.
For a fuller reproduction, archive the Python source and a Go verifier pinned to a specific commit, then record runtime and computer algebra versions. Recompute orders without the supplied cache and log CPU, elapsed time, cache status and rejection reasons. Keep existing algorithm policy in place: the National Institute of Standards and Technology deprecated binary-field curves in 2023 and strongly recommended other prime curves. Reproducing a binary seed provides no basis for reversing that guidance. Sources: NIST