#9: Implementing Stromchiffre
- Also various bug fixes and improvements
This commit is contained in:
parent
97d833349c
commit
087a01cd3d
|
@ -16,7 +16,6 @@ def create_shift_register(init_state: [int], coefficients: [int]) -> [int]:
|
||||||
"""
|
"""
|
||||||
return_list = []
|
return_list = []
|
||||||
current_state = init_state.copy()
|
current_state = init_state.copy()
|
||||||
current_state_list = [current_state.copy()]
|
|
||||||
|
|
||||||
if (size := len(init_state)) != len(coefficients):
|
if (size := len(init_state)) != len(coefficients):
|
||||||
print('Length of lists does not match.')
|
print('Length of lists does not match.')
|
||||||
|
@ -26,15 +25,18 @@ def create_shift_register(init_state: [int], coefficients: [int]) -> [int]:
|
||||||
return_list.append(current_state[-1])
|
return_list.append(current_state[-1])
|
||||||
new_state_value = 0
|
new_state_value = 0
|
||||||
for i in range(size):
|
for i in range(size):
|
||||||
new_state_value = (new_state_value + current_state[i] ^ coefficients[i]) % 2
|
new_state_value = (new_state_value + current_state[i] * coefficients[i]) % 2
|
||||||
|
|
||||||
current_state.insert(0, new_state_value)
|
current_state.insert(0, new_state_value)
|
||||||
current_state.pop(-1)
|
current_state.pop(-1)
|
||||||
|
|
||||||
if current_state in current_state_list:
|
if current_state == init_state:
|
||||||
|
# For ideal coefficients, the output is perfectly fine.
|
||||||
|
# However, for unideal coefficients, it might appear that we reach a state after lets say 1 iteration.
|
||||||
|
# We still need to output [size] amount of bits, hence the following line.
|
||||||
|
if x != (2 ** size) - 2:
|
||||||
return_list.extend(current_state[1:][::-1])
|
return_list.extend(current_state[1:][::-1])
|
||||||
break
|
break
|
||||||
current_state_list.append(current_state.copy())
|
|
||||||
|
|
||||||
return return_list
|
return return_list
|
||||||
|
|
103
chapter_three/Stromchiffre.py
Normal file
103
chapter_three/Stromchiffre.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
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))
|
|
@ -1,8 +1,10 @@
|
||||||
|
import decimal
|
||||||
|
|
||||||
LETTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
LETTERS = ['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']
|
'W', 'X', 'Y', 'Z']
|
||||||
|
|
||||||
|
|
||||||
def is_letter_of_alphabet(letter: str):
|
def is_letter_of_alphabet(letter: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if the given letter is a valid letter of the alphabet
|
Checks if the given letter is a valid letter of the alphabet
|
||||||
:param letter: The letter to check
|
:param letter: The letter to check
|
||||||
|
@ -11,7 +13,7 @@ def is_letter_of_alphabet(letter: str):
|
||||||
return letter.upper() in LETTERS
|
return letter.upper() in LETTERS
|
||||||
|
|
||||||
|
|
||||||
def get_letter_at_index(idx: int, capital: bool = False):
|
def get_letter_at_index(idx: int, capital: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the letter at the given index
|
Returns the letter at the given index
|
||||||
:param idx: The index of the letter to return
|
:param idx: The index of the letter to return
|
||||||
|
@ -24,7 +26,7 @@ def get_letter_at_index(idx: int, capital: bool = False):
|
||||||
return LETTERS[idx] if capital else LETTERS[idx].lower()
|
return LETTERS[idx] if capital else LETTERS[idx].lower()
|
||||||
|
|
||||||
|
|
||||||
def get_index_of_letter(letter: str):
|
def get_index_of_letter(letter: str) -> int:
|
||||||
"""
|
"""
|
||||||
Returns the index of the given letter
|
Returns the index of the given letter
|
||||||
:param letter: The letter to return the index of
|
:param letter: The letter to return the index of
|
||||||
|
@ -34,3 +36,16 @@ def get_index_of_letter(letter: str):
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
return LETTERS.index(letter.upper())
|
return LETTERS.index(letter.upper())
|
||||||
|
|
||||||
|
def get_binary_index_of_letter(letter: str) -> str:
|
||||||
|
"""
|
||||||
|
Returns the binary representation of the letter index
|
||||||
|
:param letter: The letter to return the index for
|
||||||
|
:return: The binary representation of the letter index
|
||||||
|
"""
|
||||||
|
char_index = get_index_of_letter(letter)
|
||||||
|
return bin(char_index)[2:].zfill(6)
|
||||||
|
|
||||||
|
def get_letter_at_binary_index(index: str) -> str:
|
||||||
|
decimal_index = int(index, 2)
|
||||||
|
return get_letter_at_index(decimal_index)
|
Loading…
Reference in New Issue
Block a user