From b8494214908004d4dd5c732ad7c7febc6cbd4a89 Mon Sep 17 00:00:00 2001 From: "dario.genga" <dario.genga@etu.hesge.ch> Date: Thu, 14 Apr 2022 13:25:53 +0200 Subject: [PATCH] Update modular_inverse and modulo. The modulo method now works with negative numbers. Also added a egcd method to get the greatest common divisor of a and b by using the euclidean division. Also added comments to all methods. --- main.py | 179 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 142 insertions(+), 37 deletions(-) diff --git a/main.py b/main.py index 27b7e65..bd6e542 100644 --- a/main.py +++ b/main.py @@ -4,14 +4,6 @@ Authors : Rayyan BELHI, Dario GENGA Date : 2022 """ -""" -Note : The methods 'get_bezout_coefficients' and 'modular_inverse' were done by Gawen ACKERMANN, Florian BURGENER, -Quentin FASLER & Dario GENGA in another lesson. -Those methods are required if the Python version is lower than 3.8. - -With Python 3.8+ we can use the built-in 'pow' function to calculate the modular inverse, where y = pow(x, -1, p). -""" - NIBBLE_BIT_SIZE = 16 MODULO_FOR_ADD = 2**4 # = 16 MODULO_FOR_MUL = MODULO_FOR_ADD + 1 # = 2^4 + 1 = 17 @@ -22,22 +14,33 @@ NB_SUBKEYS_BY_ROUND = 6 NB_SUBKEYS_FINAL_ROUND = 4 -# Return a mod b def modulo(a, b): - res = 0 - tmp = 0 - if a < b: - res = a - elif a > b: - tmp = a // b - res = a - (b*tmp) - else: - res = 0 + """Compute a mod b. + + Args: + a (int): The number a + b (int): The number b + + Returns: + int: The result of a mod b. + """ + + # mod(a, b) = a - b * floor(a / b) + res = a - b * (a // b) return res -# Return (a+b) mod 16 def add_mod(a, b): + """Compute (a+b) mod 16. + + Args: + a (str or int): The number a + b (str or int): The number b + + Returns: + str: A nibble string of (a+b) mod 16. + """ + if type(a) == str: a = int(a, BINARY_BASE) @@ -49,8 +52,17 @@ def add_mod(a, b): return format(res, STRING_BINARY_FORMAT) -# Return (a*b) mod 17 def mul_mod(a, b): + """Compute (a*b) mod 17. + + Args: + a (str or int): The number a + b (str or int): The number b + + Returns: + str: A nibble string of (a*b) mod 17. + """ + if type(a) == str: a = int(a, BINARY_BASE) @@ -73,8 +85,17 @@ def mul_mod(a, b): return format(res, STRING_BINARY_FORMAT) -# Return a string of the result of a XOR b def xor(a, b): + """Compute a XOR b. + + Args: + a (str or int): The number a + b (str or int): The number b + + Returns: + str: The result of a XOR b. + """ + res = '' # Format to binary string @@ -112,8 +133,36 @@ def xor(a, b): return res +def egcd(a, b): + """Get the greatest common divisor of a and b by using the euclidean division + + Args: + a (int): The number a + b (int): The number b + + Returns: + int: The gcd of a and b. + """ + + if a < b: + tmp = a + a = b + b = tmp + + r = b + old_r = r + + while r != 0: + old_r = r + r = modulo(a, b) + a = b + b = r + + return old_r + + def get_bezout_coefficients(a, b): - """Find the Bézout coefficients of a and b. + """Find the Bézout coefficients of a and b by using the extended euclidean algorithm. Args: a (int): The number a @@ -122,11 +171,12 @@ def get_bezout_coefficients(a, b): Returns: tuple: Bézout coefficients. """ + + i = 2 + q = [0, 0] r = [a, b] x = [1, 0] y = [0, 1] - q = [0, 0] - i = 2 while True: r.append(r[i - 2] % r[i - 1]) @@ -134,35 +184,50 @@ def get_bezout_coefficients(a, b): # Continue until the rest is equal to 0 if r[i] == 0: break - q.append(int(r[i - 2] / r[i - 1])) + + q.append(r[i - 2] // r[i - 1]) x.append(x[i - 2] - q[i] * x[i - 1]) y.append(y[i - 2] - q[i] * y[i - 1]) i += 1 + return x[-1], y[-1] -def modular_inverse(a, n): - """Compute the modular inverse of a number a modulo n. +def modular_inverse(a, b): + """Compute the modular inverse of a number a modulo b. Args: - a (int): The number to reverse. - n (int): The modulo. + a (str or int): The number to reverse. + b (str or int): The modulo. Returns: - int: The reversed number. + str: A nibble string of the reversed number. """ if type(a) == str: a = int(a, BINARY_BASE) - coefficients = get_bezout_coefficients(a, n) + x, y = get_bezout_coefficients(a, b) + bezout = a * x + b * y + + if bezout != egcd(a, b): + print("Error detected in the Bézout's identity") + return None - if a * coefficients[0] % n == 1: - return format(coefficients[0] % n, STRING_BINARY_FORMAT) + if bezout == 1 and modulo(a * x, b) == 1: + return format(x % b, STRING_BINARY_FORMAT) return None def inverse_add_mod(a): + """Get the inverse of nibble for addition modulo 16 + + Args: + a (str or int): The number to reverse. + + Returns: + str: A nibble string of the inverse. + """ if type(a) == str: a = int(a, BINARY_BASE) @@ -171,15 +236,30 @@ def inverse_add_mod(a): return format(res, STRING_BINARY_FORMAT) -# Shift the x bits starting from the left of the key to the end def shift_key(key, x=6): + """Shift the x bits starting from the left of the key to the end + + Args: + key (str): The key used for the algorithm. + x (int): The size of the shift. + + Returns: + str: The key shifted. + """ bits_to_shift = key[0:x] new_key = key[x:] + bits_to_shift return new_key -# Create the table of the 28 subkeys used for the IDEA encryption def create_subkeys_table(key): + """Create the table of the 28 subkeys used for the IDEA encryption + + Args: + key (str): The key used for the algorithm. + + Returns: + 2D array: A 2d array of the subkeys. + """ subkeys = [] subkeys_round = [] nibble = '' @@ -213,6 +293,14 @@ def create_subkeys_table(key): def create_decryption_subkeys_table(subkeys_table): + """Create the table of the 28 subkeys used for the IDEA decryption + + Args: + subkeys_table ([][]): A 2d array of the subkeys. + + Returns: + 2D array: A 2d array of the decryption subkeys. + """ decryption_subkeys = [] remaining_round = 4 @@ -244,8 +332,16 @@ def create_decryption_subkeys_table(subkeys_table): return decryption_subkeys -# Encrypt the text with the subkeys by using the simplified IDEA algorithm def encrypt(plaintext, subkeys): + """Encrypt the text with the subkeys by using the simplified IDEA algorithm + + Args: + plaintext (str): The text to encrypt + subkeys ([][]): A 2d array of the subkeys. + + Returns: + str: The encrypted text.. + """ input_block = plaintext current_round = 0 @@ -293,8 +389,17 @@ def encrypt(plaintext, subkeys): return ciphertext -# Decrypt the ciphertext with the subkeys table by using the simplified IDEA algorithm def decrypt(ciphertext, subkeys_table): + """Decrypt the ciphertext with the subkeys table by using the simplified IDEA algorithm + + Args: + ciphertext (str): The text to decrypt. + subkeys_table ([][]): A 2d array of the subkeys. + + Returns: + str: The decrypted text. + """ + # To decrypt the ciphertext, we just have to encrypt it by using the decryption subkeys table decryption_keys = create_decryption_subkeys_table(subkeys_table) return encrypt(ciphertext, decryption_keys) -- GitLab