Ciphers

Gronsfeld

Vigenère with a number for a key. Each digit shifts one letter by 0 to 9, so a PIN or a date makes a perfectly good key.
IDgronsfeld07 / 62classical · polyalphabetic
Cipher / Polyalphabetic

Gronsfeld

A PIN for a key, every digit one shift.

Keyspace
10^keyLength
Decode
same options back
Works on
A-Z, the rest passes
Family
9 in polyalphabetic
required 1 / 1

Options

keystring
required
Repeating digits 0 to 9, nothing else; 0123 is the Vigenère key ABCD

Access

Createcreate("gronsfeld")
CLIciphers encode gronsfeld GRONSFELD --key 1234
Tryplayground with the sample above
Kinvigenere, beaufort, porta, autokey +4

Gronsfeld is Vigenère with a number for a key. Each digit shifts one letter by 0 to 9, and the key repeats. So 1234 moves the first letter by one, the second by two, and so on. A PIN, a date, a phone number, the digits of pi. Who looks twice at four digits on a sticky note?

ts
const gronsfeld = create("gronsfeld");
gronsfeld.encode("GRONSFELD", { key: "1234" }).text; // "HTRRTHHPE"
gronsfeld.decode("EEREG", { key: "123" }).text; // "DCODE"

Both are the worked examples from dCode.

Why is the key a string?

Because 0123 and 123 are different keys. A number eats the leading zero, and then every letter gets the wrong shift. So key is a string of digits and nothing else. Spaces, dashes, letters and a plain 1234 number are all an InvalidOptionError. No key at all is a MissingOptionError. On the CLI --key 0123 stays a string, zero included.

The Vigenère inside

Digit d is the Vigenère key letter d places after A. Wikipedia puts it in one line: Gronsfeld 0123 is Vigenère ABCD. The rest is Vigenère too. Spaces, punctuation and anything outside A to Z pass through without moving the key. preserveCase and stripNonAlpha work the same way.

Why it's weak

Ten shifts per key position instead of 26. A five digit key has 100,000 values, fewer than Vigenère has keys of four letters. And every Vigenère attack still works. period finds the key length. recover has no Gronsfeld mode and doesn't need one:

ts
const secret = gronsfeld.encode(englishText, { key: "31415" }).text;
recoverKey(secret, { cipher: "vigenere" }).candidates[0].key; // "DBEBF"

A Vigenère key made only of A to J is a Gronsfeld key in disguise. Read it back as digits and DBEBF is 31415. Pi, of course.