physics, maths & computer science

Similar ideas to the Playfair cipher – digraphs, substitution and transposition – but in a way that suggests a dataframe approach can be used (here: pandas).

import libraries:
import pandas as pd
import numpy as np
import random
import string

get plaintext and keyword, and create the ADFCVX grid
plaintext = raw_input("enter plaintext to encipher: ")
keyword = raw_input("enter transposition key (even number of unique letters): ")

letters = string.ascii_lowercase
letters = [letter for letter in letters]
numbers = range(0,10,1)
for num in numbers:
letters.append(num)
i = 0
length = len(letters)
counter = 0
random_sequences = {}
while i < length:
j = 0
sequence = []
while j < 6:
random_selection = random.choice(letters)
letters.remove(random_selection)
random_selection = str(random_selection)
sequence.append(random_selection)
j += 1
i += 1
counter +=1
random_sequences[i] = sequence

create pandas series
index_letters = ['A','D','F','G','V','X']
df = pd.DataFrame(random_sequences,index=index_letters)
df.columns = ['A','D','F','G','V','X']
print "grid. this must be communicated to receiver for dicipherment: "
print "\n"

print df

gives:
grid. this must be communicated to receiver for dicipherment:

A D F G V X
A 2 7 u w q v
D a i 5 g m o
F n 9 h l z 1
G x d r k 8 4
V f j b e 3 y
X p c s 6 t 0

prepare plaintext
plaintext = plaintext.lower()
plaintext = plaintext.replace(" ", "")
plaintext = [letter for letter in plaintext]

stage 1: the substitution cipher

ciphertext_digraphs = []
for pt_letter in plaintext:
ciphertext_digraph_1 = ''
ciphertext_digraph_2 = ''
for col in index_letters:
mask = df[col]==pt_letter
if df[mask].index != 0:
ciphertext_digraph_1 = df[mask].index[0]
ciphertext_digraph_2 = col
ciphertext_digraphs.append(ciphertext_digraph_1)
ciphertext_digraphs.append(ciphertext_digraph_2)

stage 2: the tansposition

no_columns = len(keyword)
digraph_dict1 = {num:[] for num in range(0,no_columns,1)}
i = 0
while i < len(ciphertext_digraphs):
j = 0
while j < no_columns:
digraph_dict1[j].append(ciphertext_digraphs[i])
j += 1
i += 1

Place the letters from each digraph against a letter in in the keyword
df2 = pd.DataFrame(digraph_dict1)
column_names = [letter for letter in keyword]
df2.columns = column_names

rearrange the filled columns in alphabetical of the keyword
df2 = df2.reindex_axis(sorted(df2.columns), axis=1)

return column names to numbers for ease of iteration
df2.columns = [range(0,no_columns,1)]

Obtain the transposed sequence of graphs
ciphertext = []
i = 0
while i<no_columns:
series = df2[i]
series = list(series)
for letter in series:
ciphertext.append(letter)
i += 1

generate ciphertext as a string
ciphertext_final = ''
for letter in ciphertext:
ciphertext_final = ciphertext_final+letter
print "final ciphertext is: ",ciphertext_final

gives:
final ciphertext is: AVDAXAVAGVXVDXXDFXXDGXXD

To decipher. Assume that keyword and grid are known and already entered. split the ciphertext into a list, and prepare the key for the reverse transposition

ciphertext = [letter for letter in ciphertext_final]
length_kw = len(keyword)
keyword1 = [letter for letter in keyword]

prepare a dataframe for the reverse transposition
series_dict = {num: 0 for num in range(0,length_kw,1)}
column_length = len(ciphertext)/length_kw
index = 0
dict_index = 0
while index<len(ciphertext):
j = 0
series = []
while j < column_length:
letter_ct = ciphertext[index+j]
series.append(letter_ct)
j += 1
series_dict[dict_index] = series
index += column_length
dict_index += 1

create the pandas dataframe, and add the columns in the sorted order.
dfc = pd.DataFrame(series_dict)
dfc.columns = keyword1

rearrange the columns to the actual order of the keyword
keyword2 = [letter for letter in keyword]
columns_order = keyword2
dfc = dfc[columns_order]
numeric_columns = [range(0,length_kw,1)]
dfc.columns = numeric_columns
keyword1.sort()

conduct the transposition, and conduct reverse look-up

rev_transposition = []
i = 0
while i< len(dfc):
j = 0
while j < length_kw:
ct_letter = dfc[j][i]
rev_transposition.append(ct_letter)
j += 1
i+=1

reverse look-up on digraph pairs
plaintext = ''
i = 0
while i< len(rev_transposition):
row_lookup = rev_transposition[i]
column_lookup = rev_transposition[i+1]
pt_letter = df[column_lookup].ix[row_lookup]
plaintext = plaintext+pt_letter
i += 2
print "plaintext is: ",plaintext

gives:
plaintext is: attackat10pm