Search code examples
pythonencryptionrandomcryptographyvigenere

Randomized Vigenere Cipher with Python


To clarify before beginning: I'm aware there are similar topics, but nothing that has really offered any direct help. Also: this is a class project; so I'm not looking for anyone to code my project for me. Tips and advice is what I'm looking for. (I would go to my professor for this kind of thing, but he can't be bothered to check his e-mail.)

This program is meant to take a user supplied seed, generate a key based on the integer, then to generate a 95 x 95 matrix in which all printable ascii characters are available for encryption/decryption purposes. (Key is all alpha, and capitalized)

The kicker: all the cells must be randomized. See image below: Randomized Vigenere Matrix

I will post my code below (Python is certainly not my forte, though I will definitely take constructive criticisms.):

import random

class Vigenere(object):

    def __init__(self, seed):
        random.seed(seed)
        #string containing all valid characters
        self.symbols= """!"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^_`abcdefghijklmnopqrstuvwxyz{|}~"""
        self.eTable = [[0 for i in range(len(self.symbols))] for i in range(len(self.symbols))]
        self.dTable = [[0 for i in range(len(self.symbols))] for i in range(len(self.symbols))]
        self.keyWord = ""
        self.message = ""
        self.ciphertext = ""
        self.keywordFromSeed(seed)

    def setMessage(self,message):
        self.message = message

    #Generate psuedorandom keyword from seed
    def keywordFromSeed(self,seed):
        Letters = []

        while seed > 0:
            Letters.insert(0,chr((seed % 100) % 26 + 65))
            seed = seed // 100
        self.keyWord = "".join(Letters)
        self.buildVigenere()

    #Contructs a 95 x 95 matrix filled randomly
    def buildVigenere(self):
        n = len(self.symbols)

        #Build the vigenere matrix
        for i in range(n):
            temp = self.symbols

            for j in range(n):
                r = random.randrange(len(temp))
                self.eTable[i][j] = temp[r]

                #This line below does not fill entire matrix. Why?
                self.dTable[j][(ord(temp[r])-32)] = chr(i+32)

                temp = temp.replace(temp[r],'')

    def encrypt(self):
        for i in range(len(self.message)):
            mi = i
            ki = i % len(self.keyWord)
            self.ciphertext = self.ciphertext + self.eRetrieve(ki,mi)

    def decrypt(self):
        for i in range(len(self.message)):
            emi = i
            ki = i % len(self.keyWord)
            char = self.dRetrieve(ki,emi)
            self.ciphertext = self.ciphertext + char
        print(self.ciphertext)

    def eRetrieve(self,ki,mi):       
        row = ord(self.message[mi]) - 32
        col = ord(self.keyWord[ki]) - 32
        print(row, col)
        return self.eTable[row][col]

    def dRetrieve(self,ki,emi):
        n = len(self.symbols)
        whichRow = ord(self.keyWord[ki]) - 32
        whichCol = ord(self.message[emi]) - 32
        return(self.dTable[whichRow][whichCol])

And just in case it helps, here's my main.py:

import argparse
import randomized_vigenere as rv

def main():

    #Parse parameters
    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--mode", dest="mode", default = "encrypt", help="Encrypt or Decrypt")
    parser.add_argument("-i", "--inputfile", dest="inputFile", default = "inputFile.txt", help="Input Name")
    parser.add_argument("-o", "--outputfile", dest="outputFile", default = "outputFile.txt", help="Output Name")
    parser.add_argument("-s", "--seed", dest="seed", default =7487383487438734, help="Integer seed")
    args = parser.parse_args()

    #Set seed and generate keyword
    seed = args.seed

    #Construct Matrix

    f = open(args.inputFile,'r')
    message = f.read()
    Matrix = rv.Vigenere(seed)
    Matrix.setMessage(message)

    if(args.mode == 'encrypt'):
        Matrix.encrypt()

        Matrix.setMessage(Matrix.ciphertext)
        Matrix.decrypt()
    else:
        Matrix.decrypt()

    o = open(args.outputFile,'w')
    o.write(str(Matrix.ciphertext))

if __name__ == '__main__':
    main()

I've just been using the default seed: 7487383487438734

My Plaintext: ABCdefXYZ


Solution

  • I was able to get it working. I'll post my implementation below:

    main.py

    import argparse
    import randomized_vigenere as rv
    
    def main():
    
        #Parse parameters
        parser = argparse.ArgumentParser()
        parser.add_argument("-m", "--mode", dest="mode", default = "encrypt", help="Encrypt or Decrypt")
        parser.add_argument("-i", "--inputfile", dest="inputFile", default = "inputFile.txt", help="Input Name")
        parser.add_argument("-o", "--outputfile", dest="outputFile", default = "outputFile.txt", help="Output Name")
        parser.add_argument("-s", "--seed", dest="seed", default =7487383487438734, help="Integer seed")
        args = parser.parse_args()
    
        #Set seed and generate keyword
        seed = args.seed
    
        #Construct Matrix
        f = open(args.inputFile,'r')
        message = f.read()
        Matrix = rv.Vigenere(seed)
        Matrix.setMessage(message)
    
        if(args.mode == 'encrypt'):
            Matrix.encrypt()
        else:
            Matrix.decrypt()
    
        o = open(args.outputFile,'w')
        o.write(str(Matrix.ciphertext))
    
        print("Seed used:",Matrix.seed)
        print("Key Generated:",Matrix.keyWord)
        print("Original Message:",Matrix.message)
        print("Decoded Message:",Matrix.ciphertext)
    
    if __name__ == '__main__':
        main()

    randomized_vigenere.py

    (Be sure to add '<', '=', and '>' to the symbol list. For some reason they keep getting removed from this post.)

    import random
    
    class Vigenere(object):
    
        #Initialize Vigenere object.
            #Sets the random seed based on integer passed to the object
            #Establishes all valid symbols
            #Generates empty matrix which will contain values for encryption/decryption
            #Generates a keyword based on the integer passed to the object
        def __init__(self, seed):
            random.seed(seed)
            self.seed = seed
            self.symbols= """ !"#$%&'()*+,-./0123456789:;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
            self.Table = [[0 for i in range(len(self.symbols))] for i in range(len(self.symbols))]
            self.keyWord = ""
            self.message = ""
            self.ciphertext = ""
            self.keywordFromSeed(seed)
    
        #Sets the plaintext that will be encrypted/decrypted
        def setMessage(self,message):
            self.message = message
    
        #Generate psuedorandom keyword from seed
        def keywordFromSeed(self,seed):
            Letters = []
    
            while seed > 0:
                Letters.insert(0,chr((seed % 100) % 26 + 65))
                seed = seed // 100
            self.keyWord = "".join(Letters)
            self.buildVigenere()
    
        #Contructs a 95 x 95 matrix filled randomly with no repeats within same line
        def buildVigenere(self):
            random.seed(self.seed)
            temp = list(self.symbols)
            random.shuffle(temp)
            temp = ''.join(temp)
    
            for sym in temp:
                random.seed(self.seed)
                myList = []
                for i in range(len(temp)):
                    r = random.randrange(len(temp))
                    if r not in myList:
                        myList.append(r)
                    else:
                        while(r in myList):
                            r = random.randrange(len(temp))
                        myList.append(r)
                    while(self.Table[i][r] != 0):
                        r = (r + 1) % len(temp)
                    self.Table[i][r] = sym
    
        #Encryption function that iterates through both the message and the keyword
            #and grabs values from Table based on the ordinal value of the current
            #character being pointed to be the iterator
        def encrypt(self):
            for i in range(len(self.message)):
                mi = i
                ki = i % len(self.keyWord)
                self.ciphertext = self.ciphertext + self.eRetrieve(ki,mi)
    
        def eRetrieve(self,ki,mi):       
            row = ord(self.message[mi]) - 32
            col = ord(self.keyWord[ki]) - 32
            return self.Table[row][col]
    
        #Decryption function that iterates through both the message and the keyword
            #and grabs values from Table based on the ordinal value of the current
            #keyWord character being pointed to be the iterator, then traversing the
            #row that corresponds to that value. While traversing that row, once there
            #is a match of the message value being searched for, take the iterator value
            #and convert it to an ascii character. This is the decrypted character
        def decrypt(self):
            self.ciphertext = ""
            for i in range(len(self.message)):
                emi = i
                ki = i % len(self.keyWord)
                self.ciphertext = self.ciphertext + self.dRetrieve(ki,emi)
    
        def dRetrieve(self,ki,emi):
            n = len(self.symbols)
            whichRow = ord(self.keyWord[ki]) - 32
            for i in range(n):
                if self.Table[i][whichRow] == self.message[emi]:
                    decryptChar = chr(i + 32)
                    return(decryptChar)

    Thank you to @Eric-Levieil for assistance