Python/ciphers/fractionated_morse_cipher.py

168 lines
3.9 KiB
Python
Raw Normal View History

"""
Python program for the Fractionated Morse Cipher.
The Fractionated Morse cipher first converts the plaintext to Morse code,
then enciphers fixed-size blocks of Morse code back to letters.
This procedure means plaintext letters are mixed into the ciphertext letters,
making it more secure than substitution ciphers.
http://practicalcryptography.com/ciphers/fractionated-morse-cipher/
"""
import string
MORSE_CODE_DICT = {
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"G": "--.",
"H": "....",
"I": "..",
"J": ".---",
"K": "-.-",
"L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
" ": "",
}
# Define possible trigrams of Morse code
MORSE_COMBINATIONS = [
"...",
"..-",
"..x",
".-.",
".--",
".-x",
".x.",
".x-",
".xx",
"-..",
"-.-",
"-.x",
"--.",
"---",
"--x",
"-x.",
"-x-",
"-xx",
"x..",
"x.-",
"x.x",
"x-.",
"x--",
"x-x",
"xx.",
"xx-",
"xxx",
]
# Create a reverse dictionary for Morse code
REVERSE_DICT = {value: key for key, value in MORSE_CODE_DICT.items()}
def encode_to_morse(plaintext: str) -> str:
"""Encode a plaintext message into Morse code.
Args:
plaintext: The plaintext message to encode.
Returns:
The Morse code representation of the plaintext message.
Example:
>>> encode_to_morse("defend the east")
'-..x.x..-.x.x-.x-..xx-x....x.xx.x.-x...x-'
"""
return "x".join([MORSE_CODE_DICT.get(letter.upper(), "") for letter in plaintext])
def encrypt_fractionated_morse(plaintext: str, key: str) -> str:
"""Encrypt a plaintext message using Fractionated Morse Cipher.
Args:
plaintext: The plaintext message to encrypt.
key: The encryption key.
Returns:
The encrypted ciphertext.
Example:
>>> encrypt_fractionated_morse("defend the east","Roundtable")
'ESOAVVLJRSSTRX'
"""
morse_code = encode_to_morse(plaintext)
key = key.upper() + string.ascii_uppercase
key = "".join(sorted(set(key), key=key.find))
# Ensure morse_code length is a multiple of 3
padding_length = 3 - (len(morse_code) % 3)
morse_code += "x" * padding_length
fractionated_morse_dict = {v: k for k, v in zip(key, MORSE_COMBINATIONS)}
fractionated_morse_dict["xxx"] = ""
encrypted_text = "".join(
[
fractionated_morse_dict[morse_code[i : i + 3]]
for i in range(0, len(morse_code), 3)
]
)
return encrypted_text
def decrypt_fractionated_morse(ciphertext: str, key: str) -> str:
"""Decrypt a ciphertext message encrypted with Fractionated Morse Cipher.
Args:
ciphertext: The ciphertext message to decrypt.
key: The decryption key.
Returns:
The decrypted plaintext message.
Example:
>>> decrypt_fractionated_morse("ESOAVVLJRSSTRX","Roundtable")
'DEFEND THE EAST'
"""
key = key.upper() + string.ascii_uppercase
key = "".join(sorted(set(key), key=key.find))
inverse_fractionated_morse_dict = dict(zip(key, MORSE_COMBINATIONS))
morse_code = "".join(
[inverse_fractionated_morse_dict.get(letter, "") for letter in ciphertext]
)
decrypted_text = "".join(
[REVERSE_DICT[code] for code in morse_code.split("x")]
).strip()
return decrypted_text
if __name__ == "__main__":
"""
Example usage of Fractionated Morse Cipher.
"""
plaintext = "defend the east"
print("Plain Text:", plaintext)
key = "ROUNDTABLE"
ciphertext = encrypt_fractionated_morse(plaintext, key)
print("Encrypted:", ciphertext)
decrypted_text = decrypt_fractionated_morse(ciphertext, key)
print("Decrypted:", decrypted_text)