2023-10-08 11:05:01 +00:00
|
|
|
"""
|
|
|
|
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/
|
|
|
|
"""
|
2024-03-13 06:52:41 +00:00
|
|
|
|
2023-10-08 11:05:01 +00:00
|
|
|
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)
|