Skip to content

Create keyword_cipher.py #250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 13, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions Cryptography/src/Keyword Cipher/keyword_cipher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'''
Python Implementation Of Keyword Cipher
Author : Mohit Kumar

A keyword cipher is a form of monoalphabetic substitution. A keyword is used as the key, and it determines the letter matchings of the cipher alphabet
to the plain alphabet.Repeats of letters in the word are removed, then the cipher alphabet is generated with the keyword matching to A, B, C etc

'''

def remove_duplicates(key: str) -> str:
"""
Removes duplicate alphabetic characters in a keyword (letter is ignored after its
first appearance).
:param key: Keyword to use
:return: String with duplicates removed
>>> remove_duplicates('Hello World!!')
'Helo Wrd'
"""

key_no_dups = ""
for ch in key:
if ch == " " or ch not in key_no_dups and ch.isalpha():
key_no_dups += ch
return key_no_dups


def create_cipher_map(key: str) -> dict:
"""
Returns a cipher map given a keyword.
:param key: keyword to use
:return: dictionary cipher map
"""
# Create alphabet list
alphabet = [chr(i + 65) for i in range(26)]
# Remove duplicate characters from key
key = remove_duplicates(key.upper())
offset = len(key)
# First fill cipher with key characters
cipher_alphabet = {alphabet[i]: char for i, char in enumerate(key)}
# Then map remaining characters in alphabet to
# the alphabet from the beginning
for i in range(len(cipher_alphabet), 26):
char = alphabet[i - offset]
# Ensure we are not mapping letters to letters previously mapped
while char in key:
offset -= 1
char = alphabet[i - offset]
cipher_alphabet[alphabet[i]] = char
return cipher_alphabet


def encipher(message: str, cipher_map: dict) -> str:
"""
Enciphers a message given a cipher map.
:param message: Message to encipher
:param cipher_map: Cipher map
:return: enciphered string
>>> encipher('Hello World!!', create_cipher_map('Goodbye!!'))
'CYJJM VMQJB!!'
"""
return "".join(cipher_map.get(ch, ch) for ch in message.upper())


def decipher(message: str, cipher_map: dict) -> str:
"""
Deciphers a message given a cipher map
:param message: Message to decipher
:param cipher_map: Dictionary mapping to use
:return: Deciphered string
>>> cipher_map = create_cipher_map('Goodbye!!')
>>> decipher(encipher('Hello World!!', cipher_map), cipher_map)
'HELLO WORLD!!'
"""
# Reverse our cipher mappings
rev_cipher_map = {v: k for k, v in cipher_map.items()}
return "".join(rev_cipher_map.get(ch, ch) for ch in message.upper())


def main():
"""
Handles I/O
:return: void
"""
message = input("Enter message to encode or decode: ").strip()
key = input("Enter keyword: ").strip()
option = input("Encipher or decipher? E/D:").strip()[0].lower()
try:
func = {"e": encipher, "d": decipher}[option]
except KeyError:
raise KeyError("invalid input option")
cipher_map = create_cipher_map(key)
print(func(message, cipher_map))


if __name__ == "__main__":
import doctest

doctest.testmod()
main()