Lab 6-2: Transposition Cipher Cipher - Encrypting and Decrypting

Note: Part of this lab came from Al Sweigart’s great book, Hacking Secret Ciphers with Python: A beginner’s Guide to cryptography and computer programming with Python, available online here at Invent With Python, among his other works. Feel free to check them out if they interest you!

The Transposition Cipher

The first thing you should do is to create a new file in your unit 6 projects folder. You should name your file FILN_transposition.py, where FILN is your first initial and last name, no space.

Encrypting with the Transposition Cipher

Let’s first go over the steps to our encryption algorithm:

  1. Find the number of rows and columns for the grid based on the key and length of the message
  2. Create the table
  3. Fill the table with the characters in the message, in row-major order.
  4. Optional: fill the remaining empty characters with spaces or random characters.
  5. To get the encrypted message, read off the characters from the table in column-major order.

Let’s turn this into pseudocode:

import math
import random

def encrypt(key,message):
    # set rows (message length divided by key, rounded up)
    # set columns (same as key)
    #initialize relevant variables

    # create a 2D array as a table

    # for each row in the table
    #   for each column in the table
    #       if we haven't hit the end of the message yet
    #           fill each element with the next character in the message
    #       if we have hit the end of the message
    #           fill each element with a random character

    # for each column in the table
    #   for each row in the table
    #       combine each character into a string

    # return the encrypted message

Here, some code will be provided to you:

import math
import random

def encrypt(key,message):
    # set rows (message length divided by key, rounded up)
    # set columns (same as key)

    letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    pointer = 0
    encrypted = ''

    # create a 2D array as a table
    table = [[''] * cols for i in range(rows)]

    # for each row in the table
    #   for each column in the table
    #       if we haven't hit the end of the message yet
    #           fill each element with the next character in the message
    #       if we have hit the end of the message
    #           fill each element with a random character

    # for each column in the table
    #   for each row in the table
    #       add the character to the encrypted message

    return encrypted

Some explanation for above:

letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

This is just a string of letters, both uppercase and lowercase, so that we can randomly pull a character from it for the remaining part of our message. You can use random.choice(letters) to return a random character from the string letters.

Pointers

pointer = 0

This is a variable wea re going to use as a ‘pointer’. In CS, a pointer is exactly what it sounds like - it’s used to point at something. In our case, we want to copy our entire string into a 2D array. We need to use a for loop to cycle through the array, but those same for loops can’t be used to cycle through the string. That’s where our pointer comes into play.

Here’s an example where we want to put the word “POKEMON” into a 2D array with 2 rows and 4 columns in row-major order.

pointer pointing at position 1

Our pointer starts at zero, which means the letter we are looking at (i.e. the next letter to add to the array) is at position 0 in the string. We are going to loop through the 2D array in row-major order, which means that we go through all the columns in each row before moving onto the next row.

pointer pointing at position 1

We start adding letters to the 2D array, and every time we do, we increment our pointer so we are looking at the next letter in the string.

pointer pointing at position 1
pointer pointing at position 1

Eventually, we reach the end of the row. With our nested for loop, it will automatically move to the next row. However, this is where the purpose of having a pointer is clear - our pointer just moves on to the next letter as normal!

pointer pointing at position 1

Finally, as a last note, you have to be careful with pointers - if the pointer runs past the end of the string, and you try to access it, you will encounter an error. Becaues of this, you will have to add a conditional to handle what happens when the pointer is past the end of the string.

Continuing with Encryption

Now that pointers have been explained, you will have to use them, along with what you’ve learned about 2D arrays, to complete the rest of your the code. The given code is below:

import math
import random

def encrypt(key,message):
    # set rows (message length divided by key, rounded up)
    # set columns (same as key)

    letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    pointer = 0
    encrypted = ''

    # create a 2D array as a table
    table = [[''] * cols for i in range(rows)]

    # for each row in the table
    #   for each column in the table
    #       if we haven't hit the end of the message yet
    #           fill each element with the next character in the message
    #       if we have hit the end of the message
    #           fill each element with a random character

    # for each column in the table
    #   for each row in the table
    #       add the character to the encrypted message

    return encrypted

Decrypting with the Transposition Cipher

Let’s review the algorithm for decrypting with the transposition cipher:

  1. Find the number of rows and columns for the grid based on the key and length of the message
  2. Create the table
  3. Fill the table with the characters in the message, in column-major order.
  4. To get the encrypted message, read off the characters from the table in column-major order.

As you probably noticed, it’s very similar to the encryption algorithm, switching only the column-major and row-major ordering.

Our partially-completed pseudocode would probably look something like this:

#random and math are already imported
def decrypt(key,message):
    # set rows (message length divided by key, rounded up)
    # set columns (same as key)

    pointer = 0
    decrypted = ''

    # create a 2D array as a table
    table = [[''] * cols for i in range(rows)]

    # for each column in the table
    #   for each row in the table
    #       if we haven't hit the end of the message yet
    #           fill each element with the next character in the message
    #       if we have hit the end of the message
    #           break

    # for each row in the table
    #   for each column in the table
    #       add the character to the decrypted message

    return decrypted

Your lab assignment is to complete both the encrypted and decrypted functions.

Testing Your Program

Similar to the Caesar cipher, you can test your program by encrypting and decrypting various messages. If all is well, encrypting and decrypting the same message should give you the original message back.

For some fun, here is also a message that has been encrypted with a key of 17. Decrypt it to see what it says!

' nl dln-Icd t  o  o hhoktJhufaefn iolavy,otmpdme  wh e o es eC guevot aeesve harvt ertanre ar htserrnyde wyyidtryieRbc he troohwiaw'.sd inmis'tyalgel  v

The following space is provided in case you want to test code out or write it in the browser:

Next Section - Lab 6-3: Bruteforcing the Transposition Cipher