Playfair encrypts pairs of letters, using a 5x5 grid.
❌ Perfect Decoding
✅ Uppercase letters (A-Z)
✅ Lowercase letters (a-z)
❌ Numbers (0-9)
❌ Symbols (!@#$)
❌ Emojis (😍🤬👩🏾💻)
Numbers, Symbols, and Emojis
Numbers, symbols, and emoji are outputted as-is by this cipher.
No J’s
A limitation of encoding using a 5x5 gris, is there are only 25 potential characters. As a result, most implementations of this cipher combine i and j. This results in all j's being treated as i's when encoding and decoding.
What is “Perfect Decoding”?
Perfect Decoding is when the decoded text exactly matches the text that was encoded. This cipher does not have perfect decoding, so you’ll need to do some mental decoding.
Based on the current settings for Playfair:
key = privatecipherAlphabet = privatebcdfghklmnoqsuwxyzHello World!
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
!@#$
✨🦄✨
Gbhzhs Xnagl!
DCDTBGHOBVHFSOQVMANFPRYYZA
dcdtbghobvhfsoqvmanfpryyzuy
0123456789
!@#$
✨🦄✨
Helxlo World!
ABCDEFGHIIKLMNOPQRSTUVWXYZ
abcdefghiiklmnopqrstuvwxyzx
0123456789
!@#$
✨🦄✨
key is any lowercase string of your choice, it cannot include duplicated letters or any js.
Given a key, create a new alphabet with that key in front.
For example, with a key of private you’ll see the cipher alphabet below, in comparison to the standard English alphabet.
The keyed-alphabet (cipherAlphabet) generated by the cipher is effectively a shuffled alphabet, with the key at the beginning, that’s why the key cannot have duplicate letters.
key = 'private'
// j has been removed from the cipher alphabet
cipherAlphabet = privatebcdfghklmnoqsuwxyz
standardAlphabet = abcdefghijklmnopqrstuvwxyz
As mentioned, think of the keyed cipherAlphabet as a 5x5 grid:
p r i v a
t e b c d
f g h k l
m n o q s
u w x y z
When encoding and decoding, loop over the given string getting pairs of letters at a time. Follow any of the rules below that apply to the pair before calculating the encoded/decoded character:
j: Replace it with an ix between the lettersxWith those rules hello would be encrypted as he lx lo and juice as iu ic ex.
This pre-processing of the string is what causes this cipher to not have perfect decoding!
For each letter in the pair, you’ll need to calculate the coordinates of the letter in the grid.
Using the cipherAlphabet created, here are example 0-indexed coordinates (x, y):
h = 2,2e = 1,1l = 2,4x = 4,2The coordinates are calculated with the following formulas:
{
x: Math.floor(letterPosition / 5),
y: letterPosition mod 5
}
The formulas below for encoding and decoding reference x1, y1 and x2, y2.
x1 and y1 are the coordinates for the first character.
x2 and y2 map to the second character coordinates.
For each pair check which formula they match:
If the letters are on the same row of the grid (x1 == x2)
encodedCharacterIndex1 = (x1 * 5) + ((y1 + 1) mod 5)
encodedCharacterIndex2 = (x2 * 5) + ((y2 + 1) mod 5)
If the letters are in the same column of the grid (y1 == y2)
encodedCharacterIndex1 = ((x1 + 1) mod 5) * 5) + y1
encodedCharacterIndex2 = ((x2 + 1) mod 5) * 5) + y2
Else, the letters are on a diagonal
encodedCharacterIndex1 = (x1 * 5) + y2
encodedCharacterIndex2 = (x2 * 5) + y1
With the calculated encodedCharacterIndex1 and encodedCharacterIndex2, you can get the new encoded letter at that index in the cipher alphabet.
For each pair check which formula they match:
If the letters are on the same row of the grid (x1 == x2)
decodedCharacterIndex1 = (x1 * 5) + ((y1 - 1) mod 5)
decodedCharacterIndex2 = (x2 * 5) + ((y2 - 1) mod 5)
If the letters are in the same column of the grid (y1 == y2)
decodedCharacterIndex1 = ((x1 - 1) mod 5) * 5) + y1
decodedCharacterIndex2 = ((x2 - 1) mod 5) * 5) + y2
Else, the letters are on a diagonal
decodedCharacterIndex1 = (x1 * 5) + y2
decodedCharacterIndex2 = (x2 * 5) + y1
With the calculated decodedCharacterIndex1 and decodedCharacterIndex2, you can get the decoded letter at that index in the cipher alphabet.
modThis cipher uses mod 5 which performs the modulo (%) operation. Javascript doesn’t have proper support for mod so this formula is used:
export function mod (a, b) {
return ((a % b) + b) % b
}