#1: Implementing Homophone Chiffre
This commit is contained in:
parent
2bd8dbc6db
commit
78afa2c967
131
chapter_two/Homophone_Chiffre.py
Normal file
131
chapter_two/Homophone_Chiffre.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
import math
|
||||
import random
|
||||
|
||||
from utils import AlphabetUtils as au
|
||||
from utils import CipherUtils as cu
|
||||
|
||||
|
||||
def encrypt_text(cleartext: str, key: {}) -> str:
|
||||
"""
|
||||
Encrypts the given text with the given key
|
||||
:param cleartext: The text to encrypt
|
||||
:param key: The key to use. Has to be generated with the function below in order to work properly.
|
||||
:return: The encrypted text
|
||||
"""
|
||||
cleartext = cu.transform_invalid_chars(cleartext)
|
||||
|
||||
letter_length = key['config']['letter_length']
|
||||
|
||||
resulting = ''
|
||||
|
||||
for char in cleartext:
|
||||
possible_ciphers = key['data'][char.lower()]
|
||||
chosen_cipher = random.choice(possible_ciphers)
|
||||
resulting += str(chosen_cipher).zfill(letter_length)
|
||||
|
||||
return resulting
|
||||
|
||||
|
||||
def decrypt_text(ciphertext: str, key: {}) -> str:
|
||||
"""
|
||||
Decrypts the given ciphertext with the given key
|
||||
:param ciphertext: The text to decrypt
|
||||
:param key: The key to use. Has to be generated with the function below in order to work properly.
|
||||
:return: The decrypted text
|
||||
"""
|
||||
letter_length = key['config']['letter_length']
|
||||
|
||||
letter_split = [ciphertext[i:i + letter_length] for i in range(0, len(ciphertext), letter_length)]
|
||||
|
||||
transformed_key = _transform_key(key)
|
||||
|
||||
resulting = ''
|
||||
|
||||
for cipher_letter in letter_split:
|
||||
cleartext = transformed_key[int(cipher_letter)]
|
||||
resulting += str(cleartext)
|
||||
|
||||
return resulting
|
||||
|
||||
|
||||
def _transform_key(key: {}) -> []:
|
||||
"""
|
||||
Transforms the given key into a format that can be easier used for decryption
|
||||
:param key: The key to transform
|
||||
:return: The transformed key. Contains a list where the index is the ciphertext and the value is the cleartext
|
||||
"""
|
||||
transformed_key = [None] * int(key['config']['max_cipher'])
|
||||
|
||||
for letter in key['data'].keys():
|
||||
possible_ciphers = key['data'][letter]
|
||||
for cipher in possible_ciphers:
|
||||
transformed_key[int(cipher)] = letter
|
||||
|
||||
return transformed_key
|
||||
|
||||
|
||||
def generate_key(key_distribution: [int]) -> {}:
|
||||
"""
|
||||
Generates a key that can be used for this cipher by using the given key distribution profile
|
||||
:param key_distribution: The key distribution, e.g. number of possibilities per letter
|
||||
:return: The key as an object which contains the letter as key and the list of possible ciphers as value
|
||||
"""
|
||||
key = {
|
||||
'config': {},
|
||||
'data': {}
|
||||
}
|
||||
|
||||
# Generate the key possibilities
|
||||
amount_of_numbers = sum(key_distribution)
|
||||
key_possibilities = [i for i in range(amount_of_numbers)]
|
||||
random.shuffle(key_possibilities)
|
||||
|
||||
# Because the ciphertext does not contain of letters but of numbers instead and these are dependent on the key,
|
||||
# we need to include the fixed length of each 'letter' here
|
||||
key['config']['letter_length'] = len(str(amount_of_numbers))
|
||||
key['config']['max_cipher'] = str(amount_of_numbers)
|
||||
|
||||
# Distribute the keys to the letters
|
||||
for letter_index in range(len(key_distribution)):
|
||||
letter = au.get_letter_at_index(letter_index)
|
||||
|
||||
keys_for_this_letter = []
|
||||
|
||||
for key_possibility in range(key_distribution[letter_index]):
|
||||
keys_for_this_letter.append(key_possibilities.pop())
|
||||
|
||||
key['data'][letter] = keys_for_this_letter
|
||||
|
||||
return key
|
||||
|
||||
|
||||
def generate_key_distribution(base: str, multiplier: int) -> [int]:
|
||||
"""
|
||||
Generates a key distribution profile based on the letter frequencies of the given input text.
|
||||
Larger inputs generally result in better key distribution profiles.
|
||||
:param base: The text to use as a sort of seed
|
||||
:param multiplier: A multiplier for the possibilities per letter
|
||||
:return: The key distribution profile as a list of integers
|
||||
"""
|
||||
letter_distribution = cu.calculate_frequency(base)
|
||||
|
||||
return [math.ceil(i * 100 * multiplier) for i in letter_distribution]
|
||||
|
||||
|
||||
def get_german_key_distribution(multiplier: int) -> [int]:
|
||||
"""
|
||||
Returns the 'optimal' key distribution profile for german texts
|
||||
:param multiplier: A multiplier for the possibilities per letter
|
||||
:return: The key distribution profile as a list of integers
|
||||
"""
|
||||
return [math.ceil(i * 100 * multiplier) for i in cu.GERMAN_FREQUENCY_PROFILE]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
dist = get_german_key_distribution(10)
|
||||
print(dist)
|
||||
key = generate_key(key_distribution=dist)
|
||||
print(key)
|
||||
ciphertext = encrypt_text('BonkRocks', key)
|
||||
print(ciphertext)
|
||||
print(decrypt_text(ciphertext, key))
|
Loading…
Reference in New Issue
Block a user