Robalaban

AboutJournal

Implementing the Hill Cypher in Python

Encryption has always fascinated me, hiding a message in plain sight, however, is now easier than ever thanks to programming languages. We can automate both the encryption and decryption of data.

The following example is how to implement the hill cipher in Python with the help of Numpy. But first, I encourage you to skim through the Wikipedia page to get a feel of how it all works before continuing to the code.

TLDR: The Hill cipher takes a string of characters, it replaces them with ciphertext (via an algorithm). To encrypt the message, each block of (n) letters is multiplied by an invertible square matrix. To decrypt the message, each block is multiplied by the inverse of the matrix used for encryption.

First thing's first, establishing our main functions and imports.

import numpy as np
from numpy.linalg import inv

# Captuer the user input and transform it to ASCII characters
def get_user_input(value, msg):
    input = raw_input(msg)
    input = input.lower()
    for character in input:
        number = ord(character) - 96
        value.append(number)


# Create an empty matrix and fill it with values from the parameter
def build_matrix(matrix):
    # Create matrix
    value = np.zeros(shape=(rows, cols))

    # Append our values
    for (i, v) in zip(range(0, len(matrix)), matrix):
        r, c = i / cols, i % cols
        value[r, c] = v

    return value

The above two functions are called each time we want to transform input from the user into a square matrix. This allows for easy data manipulation and is the core of the cipher.

As is the build_matrix function won't work because we haven't got the length of our string and the upper int or our (n) in the square (nXn) matrix. To do that we'll use these helper functions below:

# Get the length of input
inputLen = len(originalMessage)
ceilInt = np.int(np.ceil(np.sqrt(inputLen)))**2

# Modulo, this is our n in nXn
modHelper = np.sqrt(ceilInt)
rows, cols = np.int(modHelper), np.int(modHelper)

With the foundation in place it is time to actually populate some variables with the users message and make use of our matrix function to prepare them for encryption.

originalMessage = []
encryptKey = []

get_user_input(originalMessage, 'Enter Message to Encrypt')
get_user_input(encryptKey, 'Enter decryption Key')

originalMessage = build_matrix(originalMessage)
encryptKey = build_matrix(encryptKey)

All that is left is the actualy encryption / decryption process. Thanks to NumPy the required code is minimal, check it out:

# Multiply our message by our encrypt key
encryptedMessage = np.dot(originalMessage, encryptKey)
print(encryptedMessage)

# Inverse the encryption in order to get the decryptionKey
decryptionKey = inv(np.matrix(encryptKey))
print(decryptionKey)

# Display decrypted message.
decryptedMessage = np.dot(originalMessage, decryptionKey)
print(decryptedMessage)

Now all together:

import numpy as np
from numpy.linalg import inv

originalMessage = []
encryptKey = []


def get_user_input(value, msg):
    input = raw_input(msg)
    input = input.lower()
    for character in input:
        number = ord(character) - 96
        value.append(number)

get_user_input(originalMessage, 'Enter Message to Encrypt')
get_user_input(encryptKey, 'Enter decryption Key')

# Get the length of input
inputLen = len(originalMessage)
ceilInt = np.int(np.ceil(np.sqrt(inputLen)))**2

# Modulo, this is our n in nXn
modHelper = np.sqrt(ceilInt)
rows, cols = np.int(modHelper), np.int(modHelper)


# Create our empty matrix and fill it with our values
def build_matrix(matrix):
    # Create matrix
    value = np.zeros(shape=(rows, cols))

    # Append our values
    for (i, v) in zip(range(0, len(matrix)), matrix):
        r, c = i / cols, i % cols
        value[r, c] = v

    return value

originalMessage = build_matrix(originalMessage)
encryptKey = build_matrix(encryptKey)

# Multiply our message by our encrypt key
encryptedMessage = np.dot(originalMessage, encryptKey)
print(encryptedMessage)

# Inverse the encryption key for awesomeness
decryptionKey = inv(np.matrix(encryptKey))
print(decryptionKey)

# Our decrypted matrix is
decryptedMessage = np.dot(originalMessage, decryptionKey)
print(decryptedMessage)

Save this to encrypt.py and run it via the terminal with: python encrypt.py. You can check the code on Github, pull requests are always welcomed.

Handmade by Robalaban