It may be easiest to just let Wikipedia explain the background of the Nihilist Cipher:
In the history of cryptography, the Nihilist cipher is a manually operated symmetric encryption cipher originally used by Russian Nihilists in the 1880s to organize terrorism against the tsarist regime.
There are three parts to this cipher:
- The keyword used to create a polybius square
- The key to encrypt the plaintext
- The plaintext to encrypt
The first step is to create the polybius square. The square was invented by ancient Greek historian and scholar Polybius as a method of fractionating text. A standard polybius square would look something like this:
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | A | B | C | D | E |
2 | F | G | H | I/J | K |
3 | L | M | N | O | P |
4 | Q | R | S | T | U |
5 | V | W | X | Y | Z |
Frequently I/J or C/K are combined in order to make it a 5×5 square. A 6×6 square could also be built, utilizing the complete alphabet and the digits 0-9. If you wanted to encode the word “KREMLIN” you just look up each letter in the table. “K” would be row 2, column 5, so it becomes “25”. The entire word would be “25 42 15 32 31 24 33”. To increase the security of the message encoded, the Nihilist Cipher uses a keyword based polybius square. A secret word, “BISHOP” in this example, is appended at the start of the alphabet, and then all remaining letters are listed in alphabetical order:
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
1 | B | I | S | H | O |
2 | P | A | C | D | E |
3 | F | G | K | L | M |
4 | N | Q | R | T | U |
5 | V | W | X | Y | Z |
With this new table, “KREMLIN” now becomes “33 43 25 35 34 12 41”.
The second part of the Nihilist Cipher is the key, which is used to encrypt the plaintext message. It is run through the polybius square as well. If it is shorter than the message it is extended through repetition. The numbers for the encoded plaintext are added to those for the encoded key and the final ciphertext is the result.
For the message “No, It’s THE code breaker. No more secrets”, a keyword of “BISHOP”, and a key of “TRON”, here is how it would work. First we create a compressed version of the message and extend the key so that it’s the same length as the message:
- Compressed message: NOITSTHECODEBREAKERNOMORESECRETS
- Extended key: TRONTRONTRONTRONTRONTRONTRONTRON
Then both are encoded using the “BISHOP” based polybius square above, which results in:
41 15 12 44 13 44 14 25 23 15 24 25 11 43 25 22 33 25 43 41 15 35 15 43 25 13 25 23 43 25 44 13 + 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 = 85 58 27 85 57 87 29 66 67 58 39 66 55 86 40 63 77 68 58 82 59 78 30 84 69 56 40 64 87 68 59 54
The final row is the ciphertext which can be passed on in a more secure fashion. Decrypting the message is done by reversing the process: subtracting the key from the ciphertext and then running it through the polybius square.
85 58 27 85 57 87 29 66 67 58 39 66 55 86 40 63 77 68 58 82 59 78 30 84 69 56 40 64 87 68 59 54 - 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 44 43 15 41 = 41 15 12 44 13 44 14 25 23 15 24 25 11 43 25 22 33 25 43 41 15 35 15 43 25 13 25 23 43 25 44 13
An example of the Nihilist Cipher in Javascript:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var letters = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; | |
// encrypt and decrypt this plain text | |
var plainText = "No Its THE code breaker No more secrets"; | |
// remove spaces | |
var compressedText = plainText.replace(/\s+/g, "").toLocaleUpperCase(); | |
var decryptedText = ""; | |
// The key needs to be repeated until it's the same length as the text to encrypt | |
var key = "TRON"; | |
var extendedKey = extendKey(key, compressedText.length); | |
// Two options for a polybius square | |
// polybius square – 5×5 = 25 letters. combine I/J | |
// polybuis square – 6×6 = 26 letters + 0-9 digits | |
// Keyword for square cannot have repetitive letters | |
// or can, but must be filtered out, which this code does | |
var keyword = "BISHOP"; | |
var polybiusLetters = keyword + letters; | |
var polybiusLetters = polybiusLetters.split('').filter(function(item, i, ar){ return ar.indexOf(item) === i; }).join(''); | |
var polybiusSquare = [[]]; | |
var index = 0; | |
for (var i = 0; i<5; i++) { | |
polybiusSquare[i] = []; | |
for (var j = 0; j<5; j++) { | |
var letter = polybiusLetters[index]; | |
polybiusSquare[i][j] = letter; | |
index++; | |
} | |
} | |
var compTextArray = buildArray(compressedText, polybiusSquare); | |
var extKeyArray = buildArray(extendedKey, polybiusSquare); | |
var cipherArray = []; | |
// create the ciphertext by adding the plaintext and key together | |
for (var i = 0; i<compTextArray.length; i++) { | |
cipherArray.push(compTextArray[i] + extKeyArray[i]); | |
} | |
var decryptArray = [] | |
// we're simply reversing the above process to decrypt | |
for (var i = 0; i<cipherArray.length; i++) { | |
decryptArray.push(cipherArray[i] – extKeyArray[i]); | |
} | |
// rebuild the original message by looking up letters in the square | |
for (var i = 0; i < decryptArray.length; i++) { | |
decryptedText += lookupLetter(decryptArray[i], polybiusSquare); | |
} | |
// Let's see if it worked | |
console.log("compressedText", compressedText); | |
console.log("decryptedText ", decryptedText); | |
// Lookup letters in the square | |
function lookupLetter(chunk, polybiusSquare) { | |
var coords = String(chunk).split(""); | |
var row = Number(coords[0]); | |
var col = Number(coords[1]); | |
// the row and col values have been increased by 1 to match the 1-5 values | |
// of a written polybius square. decrease by 1 | |
row–; | |
col–; | |
return polybiusSquare[row][col]; | |
} | |
function buildArray(letters, polybiusSquare) { | |
var returnArray = []; | |
for (var i = 0; i<letters.length; i++) { | |
var char = letters[i]; | |
returnArray.push(findChunk(char, polybiusSquare)) | |
} | |
return returnArray; | |
} | |
// find a letter in the square | |
function findChunk(findMe, polybiusSquare) { | |
for (var i = 0; i<5; i++) { | |
for (var j = 0; j<5; j++) { | |
if (polybiusSquare[i][j] === findMe) { | |
// the Polybius square has rows and columns labeled 1,2,3,4,5 | |
// so increment the row and column for display | |
i++; | |
j++; | |
return Number("" + i + j); // this is hacky, but it works | |
} | |
} | |
} | |
} | |
// extend the key to a predetermined length | |
function extendKey(key, length) { | |
var longKey = key; | |
while (longKey.length < length) { | |
longKey += key; | |
} | |
return longKey.substring(0, length); | |
} |