A permutation cipher is a very old form of cryptography. It works by moving letters around in a pre-defined pattern, so can be applied easily by hand. This also means the letter frequencies of the plaintext are preserved.

Key generation

This is a block cipher, so first decide the size of the block you want (n), in this example I'll use 5 letters. Using a block size equal to the length of the message is equivalent to just shuffling all the letters, there will be no distinguishable pattern. The longer the block length, the more secure the message. Of course, you don't have to use letters, you can use bits. The key itself consists of an ordered set of integers between 1 and n. Randomly swop pairs of numbers until you're satisfied.

1) 1 2 3 4 5
2) 5 2 3 4 1
3) 2 5 3 4 1
4) 2 5 1 4 3
5) 2 4 1 5 3

Now I'll use 5 as the key as it looks random enough, so lets get to applying it.

Enciphering

My message is:

LOREM IPSUM DOLOR SITAM ETCON SECTE TUERA DIPIS CINGE LITXX

Now in each block, I re-arrange it in the order shown in the key, so the first element in the ciphertext is the 2nd element from the plaintext. This means the ciphertext corresponding to my message and key is:

OELMR PUIMS OODRL IASMT TOENC ETSEC URTAE IIDSP IGCEN IXLXT

As you can see, the secrecy of the message isn't brilliant, some things will be easy to spot straight off. Due to the massive redundancy in human language it is sometimes possible to break a cipher just by reading it.

Deciphering

To reverse the process you need to find the inverse key; that is the key that reverts the permutation the normal key creates. The value of an element is the position of that element in the key. In this case, the key is:

[2, 4, 1, 5, 3]

and the inverse is

[3, 1, 5, 2, 4]

Code

If you want to try this cipher out yourself, here is the python implementation I wrote to speed up this write-up:

def decrypt(cipher, ciphertext):
    return encrypt(inverse_key(cipher), ciphertext)

def encrypt(cipher, plaintext):
    plaintext = "".join(plaintext.split(" ")).upper()
    ciphertext = ""
    for pad in range(0, len(plaintext)%len(cipher)*-1%len(cipher)):
        plaintext += "X"
    for offset in range(0, len(plaintext), len(cipher)):
        for element in [a-1 for a in cipher]:
            ciphertext += plaintext[offset+element]
        ciphertext += " "
    return ciphertext[:-1]

def inverse_key(cipher):
    inverse = []
    for position in range(min(cipher),max(cipher)+1,1):
        inverse.append(cipher.index(position)+1)
    return inverse

To encrypt, run:

cipher = [2,4,1,5,3]
plaintext = "LOREM IPSUM DOLOR SITAM ETCON SECTE TUERA DIPIS CINGE LITXX"
ciphertext = encrypt(cipher, plaintext)

Decrypt

To encrypt, run:

cipher = [2,4,1,5,3]
ciphertext = "OELMR PUIMS OODRL IASMT TOENC ETSEC URTAE IIDSP IGCEN IXLXT"
plaintext = decrypt(cipher, ciphertext)

Simple enough, eh?

Flaws

Brute forcing this is very easy. An example is generating every key combination and applying to the ciphertext, then looking for "THE", "TH", "HE" or other trigrams/bigrams common in the source language.

As previously stated, sometimes it's possible for a human to break the enciphering just by looking at it, as each letter of the English language has a LOT of redundancy.

Also, the string must be padded. I used "X" which is pretty obvious. This will allow people to see how long the plaintext string is. Another problem is the ciphertext length will always be a multiple of the block length.

Log in or register to write something here or to contact authors.