Krypto_Grundlagen/chapter_two/Homophone_Chiffre.py

132 lines
4.0 KiB
Python
Raw Normal View History

2021-10-17 14:54:45 +00:00
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))