VHRNE
At the end of the last post VHRNE was stuck. Large parts of it were proven, but the opening and the ending wouldn’t fit any wording I tried. Just because (most of) the wording is known doesn’t mean the puzzle is solved!
The next evening it broke, and the reason it wouldn’t fit turned out to be the cipher, not the wording.
The message
VHRNE is message Nr. 284 on CryptoCellar’s list: 136 letters, sent by the division’s supply commander at 06:30 on 31 July 1941. The quartermaster’s clear-text log in the Bundesarchiv (RS 3-3/47b, fol. 241) tells us what it says:
Gestern gingen von PLESKAU ein: 1116 F.H.Gr. A.Z. PLESKAU gibt nichts mehr ab. HARTJENSTEIN.
“Yesterday 1116 field-howitzer shells with impact fuzes came in from Pleskau. Pleskau isn’t issuing any more. Hartjenstein.”
So we knew the content. The problem was getting from that to the exact letters the clerk enciphered.
The ciphertext gives most of it away
Look at the ciphertext in letter pairs and some of the repeats jump out:
VH RN EU SP EE VH SQ AL AH LI TR KH AH LI TR KH CZ EE CZ EE RZ EE RZ EE …
AH LI TR KH appears twice in a row, and the same eight pairs come back again near the end. That’s a place name, doubled with X’s the way these clerks liked to: X PLESKAU X PLESKAU. Then CZ EE CZ EE RZ EE RZ EE lines up exactly with XE IN XE IN SE IN SE IN, the start of X EIN X EINS EINS EINS SEQS (“ein: 1116”). EE turns up seven times in total, and everywhere the wording is known it stands for IN: GINGEN, EIN, EINS three times, and STEIN at the very end.
Three independent bits of the message (the opening, the number block and the signature) all agreed on the same pairs. That pinned about 99 of the 136 letters before any key search. The only stretch we couldn’t guess was the 37 letters in the middle, where “F.H.Gr. A.Z.” had to go.
Not your normal cup of tea
The trouble was that this wording didn’t fit the Truppenschlüssel, the single-stage two-square cipher this net used on every other July day I’d broken. It’s impossible, and you can show that without searching at all.
In a two-square, the first cipher letter of a pair comes from the left box: the row of the first plaintext letter, the column of the second. So every plaintext pair ending in E produces its first cipher letter from the same five-cell column of the left box. The wording has six different pairs ending in E, and they encipher to six different first letters. Six letters don’t fit in five cells. No key, however clever, can do it.
I’d spent days assuming I had the wording wrong ( maybe everyone else had too ). The counting argument said the wording was fine, and the cipher was the problem.
What else could it be?
At this point I set a few fresh Claude agents on it, with a brief that left out all of our earlier conclusions so they couldn’t inherit our assumptions. One looked at the plaintext, one at the archive readings, and one questioned the cipher itself.
The one questioning the cipher worked through the alternatives: Playfair, four-square, the boxes copied with a letter missing or duplicated, two known daily keys combined, seriation, transposition, a hidden indicator group, a key change mid-message. Each was either impossible or no better than random control wordings. One model survived: the official Doppelkasten. That’s the same two boxes, but every pair is enciphered twice. The 1940 and 1941 instructions prescribe exactly that, plus a 17-column rearrangement this message clearly didn’t use, because its repeats sit in plain consecutive pairs.
The GPU gives up
The horrific CUDA code from last time was no use here. I even made a fake two-stage message with a key I knew, gave it the same crib, and it still got nowhere. Enciphering twice means a tiny change to the key changes everything, so there’s nothing for it to climb towards.
Instead, Claude threw a SAT solver at it ( CaDiCaL, which I’d never heard of ). Rather than guessing keys and scoring them, you tell it the rules, and it works out whether any key can satisfy them. We only gave it the 20 pairs we were sure of: the opening, the PLESKAU blocks, the numbers and the signature. The 37 letters in the middle were left out entirely.
It came back in under three minutes, after days of the GPU getting nowhere. It gave a handful of keys that only disagreed about where M, W and Y went.
Success?
The real test was the middle. Nobody had told the solver anything about letters 53 to 89, and one of the keys decrypted them to this:
FRITZ HEINRICH GRANATEN X ANTON X ZEPPELIN
That’s F.H.Gr. A.Z., spelled out in the Wehrmacht’s phonetic alphabet. Forty-odd letters of fluent German, matching the log, appearing out of a part of the message the solver never saw, doesn’t happen by accident. The full message:
GESTERN GINGEN VON X PLESKAU X PLESKAU X EIN X EINS EINS EINS SEQS FRITZ HEINRICH GRANATEN X ANTON X ZEPPELIN X X PLESKAU X PLESKAU X GIBT NIQTS MEHR AB X HARTIIENSTEIN
Encrypting that twice with the key reproduces the sender’s copy at all 136 positions. Decrypting it only once with the same key gives garbage, so the second stage is real. With the whole plaintext, the solver finds exactly one key, up to the usual row and column shuffles. The received copy that CryptoCellar has differs from the sender’s in a handful of letters, all reception errors (mostly Q written as G).
The same clerk, a different cipher
The odd thing is that this clerk normally didn’t do this. Along the way we also recovered the 3 August key from a 234-letter message in the same handwriting, and that one is plain single-stage TS with only a couple of slips. So on 31 July, for one message, the supply commander’s office used the full double encipherment, without the rearrangement the instructions called for.
After I disclosed the break Olaf pointed out that this net was using Enigma, the TS and the Doppelkasten side by side, and that a Doppelkasten without the rearrangement hadn’t been seen before. Frode and Olaf confirmed the break, and CryptoCellar now lists VHRNE as broken.
Next
So far all of this work has been done with some janky code and ~40GB of scans from the Bundesarchiv, hundreds of transcription files in a dozen formats, key write-ups and notes, but it’s time to step up our game.
So I’m building a proper pipeline:
- A shared GPU/CPU job dispatcher, agentq. It’s a small persistent service that schedules jobs for every agent and project.
- Breaking work comes first, but bulk transcription is guaranteed a share of the GPU.
- Agents take turns, and CPU cores are budgeted.
- Each job runs as its own systemd unit, so jobs survive restarts and timeouts and reboots are reported accurately.
- A records store in Neo4j. The domain is naturally a graph: archive sheets, messages, copies, readings, keys, clear texts, relays and notes.
- One message can be known by five different identifiers (indicator, CryptoCellar number, German message number, date and time, archive folio). Aliases resolve all of them.
- Every reading carries a trust level, from “machine guess” up to “confirmed by a clean decrypt”.
- A
cctool offers lookup, fuzzy ciphertext search that tolerates handwriting confusions like h/s and n/m, full-text search, and graph queries. For example, “unbroken messages on a day whose key is known” or “the same text sent in two cipher systems”, which is a ready-made crib.
- Ingest. Everything already transcribed and broken goes in first, then a local vision model builds a map of which archive volumes cover which dates.
- Transcription. Cheap local models produce candidate letters, and an Opus-tier transcriber agent verifies only what matters, starting with open targets. Those verified glyphs later feed nearest-neighbour letter reading and writer identification.
I’m hoping with this new suite of tools we’ll be able to break something much harder, “The Ultimate Enigma Challenge”
There are still a handful of TS messages open on CryptoCellar’s list. Most of them are stuck for the opposite reason: we know the cipher, but nothing in the archive tells us what they say.