Skipjack's S-box and Seed 48571
One line of srand48 reproduces two-thirds of the NSA cipher’s lookup table. Here is what that shows, what it doesn’t, and an interactive way to see the difference.
In short. Seeding the POSIX
drand48generator with 48571 and running a simple shuffle produces a table that matches 173 of 256 bytes of Skipjack’s F-table. Neighbouring seeds match 1. The remaining 83 bytes can be fixed with 52 swaps, but those swaps are read off the official table, so they would “fix” any seed. The match is the evidence; the swaps are bookkeeping. One thing the swaps do reveal: they raise nonlinearity from 92 to 100 while differential uniformity gets slightly worse, from 10 to 12.
Background
Skipjack is the 64-bit block cipher NSA designed for the Clipper chip. It was declassified in 1998, and NIST published the specification, including a single 8-bit substitution table called the F-table. NSA never said how that table was made. For an S-box from an intelligence agency, “where did these 256 bytes come from?” is a fair question, because unexplained constants are exactly where a designer could hide structure.
The claim
On 17 October 2026, Steve Weis posted that Claude and Ava Howell had found Skipjack’s S-box comes from srand48(48571) “plus some swaps”. Howell then posted the generator. David Adrian’s crypto-conspiracy-bench repository reproduced it independently against the NIST specification and documented its limits carefully.
int F[256], k = 0;
for (int i = 0; i < 256; i++) F[i] = -1;
srand48(48571);
while (k < 256) {
int v = 256 * drand48();
if (F[v] < 0) F[v] = k++;
}
Each draw takes the top byte of drand48’s 48-bit state as a slot. The first time a slot is hit, it is given the next number in sequence. Repeat hits are ignored. After 1,399 draws, every slot is filled and the result is a permutation of 0–255.
What the numbers say
| Table | Matches Skipjack |
|---|---|
| Seed 48571 | 173 / 256 |
| Seed 48570 (control) | 1 / 256 |
| Seed 48572 (control) | 1 / 256 |
| Expected for any random permutation | ≈ 1 / 256 |
Matching 173 positions by accident is not plausible. The first nine bytes, a3 d7 09 83 f8 48 f6 f4 b3, line up exactly, and long runs agree throughout the table. Whatever process NSA used, seed 48571 and this shuffle are very close to it.
Try it
Generate the table, compare it with Skipjack, then play the swaps slowly. Each swap is shown in four stages: find a wrong cell, look up the answer key, find where the right value sits, and swap.
Runs the real drand48 arithmetic in your browser. Compare seed 48571 with 48570: both end as Skipjack after swapping, but only one gets there mostly on its own.
Why the 52 swaps prove nothing
The swaps are found by walking the 256 positions. Whenever a value is wrong, the procedure looks up the correct value in the official table and swaps it into place. The target is the answer key for every correction.
That works for any starting table. Seed 48570 matches only one byte, yet the same procedure still produces a perfect Skipjack table; it just needs about 250 swaps. Mathematically, turning one permutation into another always takes 256 minus the number of cycles in the difference between them. “Matches after swaps” is guaranteed, so it carries no information.
An analogy: guess a 256-word sentence and get 173 words in the right places. Then check the original and correct the other 83. The perfect final sentence says nothing about how you guessed. Only the 173 do.
The number 52 is also not necessarily what NSA did. It is one minimal reconstruction. Many edit sequences reach the same final table, so “some swaps” in the original claim does not identify a procedure.
What the swaps do change
Even though the swaps don’t explain themselves, the gap between the raw generator output and the published table is informative:
| Property | Seed 48571 raw | Skipjack F-table | AES S-box |
|---|---|---|---|
| Nonlinearity (higher is better) | 92 | 100 | 112 |
| Max absolute Walsh coefficient | 72 | 56 | 32 |
| Differential uniformity (lower is better) | 10 | 12 | 4 |
| Fixed points | 0 | 0 | 0 |
The edits move nonlinearity from typical-random territory (90–94) to 100, while differential uniformity slightly worsens. That pattern fits a designer who drew a random table and then adjusted it to resist linear cryptanalysis specifically. It is a hypothesis, not a finding: the order in which the swaps are applied in the widget is arbitrary, so only the start and end points are meaningful.
A pattern in the fixes
Looking at the differences in value space rather than position: 22 of the 83 wrong bytes are off by exactly one, and eight of the corrections simply exchange consecutive ranks, such as 06↔07, 21↔22, 37↔38 and 78↔79. Arbitrary hand edits would rarely look like that. Small local reorderings of nearby ranks would.
What would count as evidence
A rule stated without looking at Skipjack that turns the seed-48571 table into the exact F-table. For example, “swap adjacent ranks whenever that raises nonlinearity”, or “re-draw until some property holds”. If a short, independently stated procedure lands on all 256 bytes, the generation method is recovered. Until then, the honest summary is: the seed is real, and the swaps are unexplained.
None of this says anything either way about a backdoor. A known seed is a weak form of “nothing up my sleeve”, but the unexplained edits are where any intent would live.
Reproduce it
Clone the repository and run python3 experimental/skipjack/verify.py. It compiles the posted C, checks it against an independent integer implementation of drand48, compares all 256 bytes with the NIST table, and prints every mismatch and the derived swaps. Its output matched the committed results exactly when I ran it.