104 lines
2.8 KiB
Python
104 lines
2.8 KiB
Python
|
import random
|
||
|
|
||
|
from utils import AlphabetUtils as au
|
||
|
from utils import CipherUtils as cu
|
||
|
from chapter_three import LinearesSchieberegister as lsr
|
||
|
|
||
|
|
||
|
def encrypt_text(cleartext: str, coefficients: [int], key: [int]) -> str:
|
||
|
cleartext_bits = []
|
||
|
|
||
|
for char in cleartext:
|
||
|
binary_index = au.get_binary_index_of_letter(char)
|
||
|
binary_index_list = []
|
||
|
for bit in binary_index:
|
||
|
binary_index_list.append(int(bit))
|
||
|
cleartext_bits.extend(binary_index_list)
|
||
|
|
||
|
keystream = lsr.create_shift_register(key, coefficients)
|
||
|
|
||
|
cipher_bits = cu.xor_two_lists(cleartext_bits, keystream)
|
||
|
|
||
|
cipher_bits_string = ''.join([str(x) for x in cipher_bits])
|
||
|
|
||
|
return cipher_bits_string
|
||
|
|
||
|
|
||
|
def decrypt_text(cipher_bits: str, coefficients: [int], key: [int]) -> str:
|
||
|
cipher_bits_list = [int(cipher_bits[i:i + 1]) for i in range(0, len(cipher_bits), 1)]
|
||
|
|
||
|
keystream = lsr.create_shift_register(key, coefficients)
|
||
|
|
||
|
cleartext_bits = cu.xor_two_lists(cipher_bits_list, keystream)
|
||
|
|
||
|
letter_split = [cleartext_bits[i:i + 6] for i in range(0, len(cleartext_bits), 6)]
|
||
|
|
||
|
cleartext = ''
|
||
|
|
||
|
for letter in letter_split:
|
||
|
# Converts the list of integers to a single string
|
||
|
letter_string = ''.join([str(x) for x in letter])
|
||
|
cleartext += au.get_letter_at_binary_index(letter_string)
|
||
|
|
||
|
return cleartext
|
||
|
|
||
|
|
||
|
def get_optimal_shift_register_coefficients(size: int) -> [int]:
|
||
|
"""
|
||
|
Returns a set of coefficients for a linear shift register with the given size that guarantees a maximum bit stream.
|
||
|
:param size: The size of the linear shift register
|
||
|
:return: The optimal coefficients
|
||
|
"""
|
||
|
match size:
|
||
|
case 1:
|
||
|
return [1]
|
||
|
case 2:
|
||
|
return [1, 1]
|
||
|
case 3:
|
||
|
return [0, 1, 1]
|
||
|
case 4:
|
||
|
return [0, 0, 1, 1]
|
||
|
case 5:
|
||
|
return [0, 0, 1, 0, 1]
|
||
|
case 6:
|
||
|
return [0, 0, 0, 0, 1, 1]
|
||
|
case 7:
|
||
|
return [0, 0, 0, 0, 0, 1, 1]
|
||
|
case 8:
|
||
|
return [0, 1, 1, 0, 0, 0, 1, 1]
|
||
|
case 9:
|
||
|
return [0, 0, 0, 0, 1, 0, 0, 0, 1]
|
||
|
case 10:
|
||
|
return [0, 0, 0, 0, 0, 0, 1, 0, 0, 1]
|
||
|
case 11:
|
||
|
return [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1]
|
||
|
case 12:
|
||
|
return [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1]
|
||
|
case 13:
|
||
|
return [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1]
|
||
|
case 14:
|
||
|
return [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
|
||
|
case 15:
|
||
|
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
|
||
|
case _:
|
||
|
return []
|
||
|
|
||
|
|
||
|
def generate_random_key(size: int) -> [int]:
|
||
|
"""
|
||
|
Generates a random key (initial filling of a linear shift register) of the desired length
|
||
|
:param size: The length of the key (must equal the length of the wanted linear shift register)
|
||
|
:return: The key
|
||
|
"""
|
||
|
return [random.randint(0, 1) for x in range(size)]
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
size = 15
|
||
|
coefficients = get_optimal_shift_register_coefficients(size)
|
||
|
key = generate_random_key(size)
|
||
|
print(key)
|
||
|
encrypted = encrypt_text('BonkRocks', coefficients, key)
|
||
|
print(encrypted)
|
||
|
print(decrypt_text(encrypted, coefficients, key))
|