Project 2
Page 1 of 3
CS303E Project 2
A Substitution Cipher Class
In
cryptography, a Substitution Cipher is one of the simplest and most
widely known encryption techniques. Each symbol in the alphabet is
replaced by
another symbol in the same (or different) alphabet. For example, consider the following picture:
Notice
that the top line is simply the standard English alphabet. The second
line is the "key" for the encryption because it tells you how to
transform
each letter. For this assignment, the alphabet and the key
should both be strings of length 26, containing only lowercase letters.
When encrypting a text
(the "plaintext"), you take each letter in
the text, find it in the top line, note the corresponding letter in the
key, and write that down. The resulting text is
the "ciphertext." Nonletters are not changed.
When
decrypting, you simply reverse the roles of the two lines. I.e., for
each letter in the ciphertext, find it's position in the key, note the
corresponding
letter in the alphabet, and write that down. Voila! You get the plaintext back.
For
this all to work, the key must be a permutation of the alphabet. That
is, it should have exactly the same letters as the alphabet, but in
scrambled
order.
Assignment:
Your task in this project is to
implement a substitution cipher. There are two main parts to this: the
SubstitutionCipher class itself, and a main()
driver routine which calls it. You must use a class for the substitution cipher itself.
The SubstitutionCipher class
Here are the specific requirements for your SubstitutionCipher class:
1.
Implement your substitution cipher as a class, with the key as a
private data member. When you create a SubstitutionCipher object you can
pass in the key, which is just a string of length 26 containing all
lowercase letters in the English alphabet in arbitrary order. If the
key is not
supplied, default to a randomly generated key. If you do pass in a key, validate that the key is legal.
2. You should have at least the following methods in your class:
__init__( key ): the initializer for the class
getKey(): the getter for the class key
setKey( key ): the setter for the class key
encryptText( text ): return the encryption (string) of the plaintext (string) argument with respect to the key
decryptText( text ): return the decryption of the ciphertext (string) argument with respect to the key
Of course, the definitions of these will need the self parameter. See the Program Structure section below.
3.
When encrypting or decrypting preserve case. That is, corresponding
plaintext and ciphertext letters should have the same case (uppercase or
lowercase). If the character is a nonletter, just return it unchanged.
4.
You can have additional methods in the class if needed. You can also
define auxiliary functions outside the class. For example, you should
define
the function makeRandomKey(); I've given you some code for
this below. There's no reason for this to be a class method; it should
be a function at
the top level but can be called within the class definition.
Hints:
It might be a good idea to define the functionality of encryption and
decryption as top level functions initially. I.e., make sure that you
understand
how everything works before you try to put it into the framework of a
class. You'll probably write several auxiliary functions; most of which
probably
can stay outside the class. In particular, notice that encryption and
decryption are really the same operation with respect to two strings. If
you're clever, you can define the encryptText and decryptText
methods as two calls to the same auxiliary function with the parameters
swapped.
The main() Driver Function:
In addition to the class
described above, you will define a main function that makes use of the
class. This function should do the following:
1. Create a new
SubtitutionCipher object. (Below let's assume it's called cipher.) The
initial key should be randomly generated.
2. In a loop, accept from
the user any of the following commands: getkey, changekey, encrypt,
decrypt, quit. Case doesn't matter for these
2021/4/1, 1(47 PMProject 2
Page 2 of 3
commands.
(Hint: lowercase the string the user types and always compare to that.)
Execute each command as entered. Execution of some of them
may involve a nested loop.
3. Commands behave as follows:
Command getkey displays the current key stored in cipher.
Command quit prints a Goodbye message (see sample output) and exits the loop.
Command
changekey allows the user to change the stored key. The user is given
multiple attempts to get this right. The user may enter any
of the
following: quit (give up and leave the key unchanged), random (generate
and store a new random key), a legal key (check if it's
legal and store if so, else try again), anything else (indicate an error and try again).
Command encrypt: ask the user for a text, encrypt and print the result using the key stored in cipher.
Command decrypt: ask the user for a text, decrypt and print the result using the key stored in cipher.
Anything else: print an error and try again in the outer loop.
Possible Program Structure
Below
is a possible structure for your program. You don't have to follow this
exactly. But you must have the SubstitutionCipher class with at least
the indicated methods, and a main routine.
import random
# A global constant defining the alphabet.
LCLETTERS = "abcdefghijklmnopqrstuvwxyz"
# You are welcome to use the following two auxiliary functions, or
# define your own. You don't need to understand this code at this
# point in the semester.
def makeRandomKey():
# This will generate a legal random key.
lst = list( LCLETTERS )
random.shuffle( lst )
return ''.join( lst )
def isLegalKey( key ):
# A key is legal if it's a lowercase string of length 26
# and contains exactly the letters of the English alphabet.
return ( len(key) == 26 and key.islower \
and all( [ ch in LCLETTERS for ch in key ] ) )
# There may be some additional auxiliary functions defined here.
# I had several others, mainly used in encrypt and decrypt.
class SubstitutionCipher:
def __init__ (self, key = makeRandomKey() ):
"""Create an instance of the cipher with stored key, which
defaults to random."""
# Note that these are the required methods, but you may define
# additional methods if you need them. (I didn't need any.)
def getKey( self ):
"""Getter for the stored key."""
def setKey( self, newKey ):
"""Setter for the stored key."""
def encryptText( self, text ):
"""Return the plaintext encrypted with respect to the stored key."""
def decryptText( self, text ):
"""Return the ciphertext decrypted with respect to the stored
key."""
def main():
""" This implements the top level command loop. It
creates an instance of the SubstitutionCipher class and allows the user
to invoke within a loop the following commands: getKey, changeKey,
encrypt, decrypt, quit."""
main()
Expected Output:
Below
is a sample output for this program. You should match this, except that
your keys will be different. Note that this is run from the command
line.
You can run yours from your IDE, but the TAs should be able to
run it from the command line. Each level is indented two spaces from
the level above
it.
> python Project2.py
Enter a command (getKey, changeKey, encrypt, decrypt, quit): getKEY
2021/4/1, 1(47 PMProject 2
Page 3 of 3
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (getKey, changeKey, encrypt, decrypt, quit): changekey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: ljljljljljljl
Illegal key entered. Try again!
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: random
New cipher key: rpqusjznvetfdiybclaomgwxkh
Enter a command (getKey, changeKey, encrypt, decrypt, quit): getkey
Current cipher key: rpqusjznvetfdiybclaomgwxkh
Enter a command (getKey, changeKey, encrypt, decrypt, quit): changekey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: bapldjgktyisxrnuvwomqhzecf
New cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (getKey, changeKey, encrypt, decrypt, quit): getkey
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (getKey, changeKey, encrypt, decrypt, quit): ChANGekey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: quit
Enter a command (getKey, changeKey, encrypt, decrypt, quit): getkey
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (getKey, changeKey, encrypt, decrypt, quit): encrypt
Enter a text to encrypt: Now is the time for all good folks to come to the aid of their country!
The encrypted text is: Rnz to mkd mtxd jnw bss gnnl jnsio mn pnxd mn mkd btl nj mkdtw pnqrmwc!
Enter a command (getKey, changeKey, encrypt, decrypt, quit): decrypt
Enter a text to decrypt: Rnz to mkd mtxd jnw bss gnnl jnsio mn pnxd mn mkd btl nj mkdtw pnqrmwc!
The decrypted text is: Now is the time for all good folks to come to the aid of their country!
Enter a command (getKey, changeKey, encrypt, decrypt, quit): arglebargle
Command not recognized. Try again!
Enter a command (getKey, changeKey, encrypt, decrypt, quit): Enter a command (getKey, changeKey, encrypt, decrypt, quit): quit
Thanks for visiting!
Note,
that if you comment out the call to main(), you should also be able to
use your SubstitutionCipher class directly. It might be a good idea to
get
that running before you build your top level driver routine. Here's what that would look like:
> python
>>> from Project2 import *
>>> cipher = SubstitutionCipher()
>>> cipher.getKey()
'cmaukidjtvlnpgbehowxzryfqs'
>>> text = "Now is the time for all good men to come to the aid of their country."
>>> text
'Now is the time for all good men to come to the aid of their country.'
>>> cipher.encryptText( text )
'Gby tw xtpk xjk ibo cnn dbbu pkg xb abpk xb xjk ctu bi xjkto abzgxoq.'
>>> ciphertext = cipher.encryptText( text )
>>> ciphertext
'Gby tw xtpk xjk ibo cnn dbbu pkg xb abpk xb xjk ctu bi xjkto abzgxoq.'
>>> cipher.decryptText( ciphertext )
'Now is the time for all good men to come to the aid of their country.'
>>> key = makeRandomKey()
>>> key
'cglnypqdsaxjvrwfmhbtkeuoiz'
>>> cipher2 = SubstitutionCipher("abcdfgh")
Key entered is not legal
>>> cipher2 = SubstitutionCipher( key )
>>> cipher2.getKey()
'cglnypqdsaxjvrwfmhbtkeuoiz'
>>> cipher2.encryptText("ABCdef")
'CGLnyp'
>>> cipher2.decryptText("CGLnyp")
'ABCdef'
>>>
学霸联盟