This shows you the differences between two versions of the page.
|
ic:labs:04 [2023/10/01 23:21] razvan.smadu [SPN 1 (3p)] |
ic:labs:04 [2023/10/09 23:21] (current) razvan.smadu |
||
|---|---|---|---|
| Line 3: | Line 3: | ||
| Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1mjbkxFqYNB-lRRXGAhsdh1vK9nwbnDuS/view?usp=sharing|aici]]. | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1mjbkxFqYNB-lRRXGAhsdh1vK9nwbnDuS/view?usp=sharing|aici]]. | ||
| - | Puteți lucra acest laborator folosind și platforma Google Colab, accesând [[https://colab.research.google.com/drive/1rxZ9eyzMXqq3NDia22suPO_1QPAb5o0C?usp=sharing | + | Puteți lucra acest laborator folosind platforma Google Colab, accesând [[https://colab.research.google.com/github/ACS-IC-labs/IC-labs/blob/main/labs/lab04/lab4.ipynb |
| |acest]] link. | |acest]] link. | ||
| + | <hidden> | ||
| <spoiler Click pentru a vedea utils.py> | <spoiler Click pentru a vedea utils.py> | ||
| <file python utils.py> | <file python utils.py> | ||
| import base64 | import base64 | ||
| + | from typing import Generator | ||
| - | # CONVERSION FUNCTIONS | ||
| - | def _chunks(string, chunk_size): | ||
| - | for i in range(0, len(string), chunk_size): | ||
| - | yield string[i:i+chunk_size] | ||
| - | # THIS ONE IS NEW | + | def _pad(data: str, size: int) -> str: |
| - | def byte_2_bin(bval): | + | reminder = len(data) % size |
| - | """ | + | if reminder != 0: |
| - | Transform a byte (8-bit) value into a bitstring | + | data = "0" * (size - reminder) + data |
| + | return data | ||
| + | |||
| + | |||
| + | def _chunks(data: str, chunk_size: int) -> Generator[str, None, None]: | ||
| + | data = _pad(data, chunk_size) | ||
| + | for i in range(0, len(data), chunk_size): | ||
| + | yield data[i : i + chunk_size] | ||
| + | |||
| + | |||
| + | def _hex(data: int) -> str: | ||
| + | return format(data, "02x") | ||
| + | |||
| + | |||
| + | # Conversion functions | ||
| + | |||
| + | |||
| + | def byte_2_bin(bval: int) -> str: | ||
| + | """Converts a byte value to a binary string. | ||
| + | |||
| + | Args: | ||
| + | bval (int): | ||
| + | The byte value to be converted. It should be an integer between | ||
| + | 0 and 255. | ||
| + | |||
| + | Returns: | ||
| + | str: The binary string representation of the byte value, where each bit | ||
| + | is encoded as a character. The result has a fixed length of 8 characters | ||
| + | and is padded with leading zeros if necessary. | ||
| + | |||
| + | Examples: | ||
| + | >>> byte_2_bin(72) | ||
| + | '01001000' | ||
| + | >>> byte_2_bin(66) | ||
| + | '01000010' | ||
| """ | """ | ||
| return bin(bval)[2:].zfill(8) | return bin(bval)[2:].zfill(8) | ||
| - | def _hex(x): | ||
| - | return format(x, '02x') | ||
| - | def hex_2_bin(data): | + | def hex_2_bin(data: str) -> str: |
| - | return ''.join(f'{int(x, 16):08b}' for x in _chunks(data, 2)) | + | """Converts a hexadecimal string to a binary representation. |
| - | def str_2_bin(data): | + | Args: |
| - | return ''.join(f'{ord(c):08b}' for c in data) | + | data (str): The hexadecimal string to be converted. It should have an |
| + | even number of characters and only contain valid hexadecimal digits | ||
| + | (0-9, A-F, a-f). | ||
| - | def bin_2_hex(data): | + | Returns: |
| - | return ''.join(f'{int(b, 2):02x}' for b in _chunks(data, 8)) | + | str: The binary representation of the hexadecimal string, where each |
| + | pair of hexadecimal digits is encoded as an 8-bit binary number. | ||
| - | def str_2_hex(data): | + | Examples: |
| - | return ''.join(f'{ord(c):02x}' for c in data) | + | >>> hex_2_bin("01abcd") |
| + | '000000011010101111001101' | ||
| + | >>> hex_2_bin("0a") | ||
| + | '00001010' | ||
| + | """ | ||
| + | return "".join(f"{int(x, 16):08b}" for x in _chunks(data, 2)) | ||
| - | def bin_2_str(data): | ||
| - | return ''.join(chr(int(b, 2)) for b in _chunks(data, 8)) | ||
| - | def hex_2_str(data): | + | def bin_2_hex(data: str) -> str: |
| - | return ''.join(chr(int(x, 16)) for x in _chunks(data, 2)) | + | """Converts a binary string to a hexadecimal representation. |
| - | # XOR FUNCTIONS | + | Args: |
| - | def strxor(a, b): # xor two strings, trims the longer input | + | data (str): The binary string to be converted. It should have a multiple |
| - | return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)) | + | of 8 characters and only contain valid binary digits (0 or 1). |
| - | def bitxor(a, b): # xor two bit-strings, trims the longer input | + | Returns: |
| - | return ''.join(str(int(x) ^ int(y)) for (x, y) in zip(a, b)) | + | str: The hexadecimal representation of the binary string, where each |
| + | group of 8 binary digits is encoded as a pair of hexadecimal digits. | ||
| - | def hexxor(a, b): # xor two hex-strings, trims the longer input | + | Examples: |
| - | return ''.join(_hex(int(x, 16) ^ int(y, 16)) for (x, y) in zip(_chunks(a, 2), _chunks(b, 2))) | + | >>> bin_2_hex("000000011010101111001101") |
| + | '01abcd' | ||
| + | >>> bin_2_hex("00001010") | ||
| + | '0a' | ||
| + | """ | ||
| + | return "".join(f"{int(b, 2):02x}" for b in _chunks(data, 8)) | ||
| - | # BASE64 FUNCTIONS | ||
| - | def b64decode(data): | ||
| - | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | ||
| - | def b64encode(data): | + | def str_2_bin(data: str) -> str: |
| + | """Converts a string to a binary representation. | ||
| + | |||
| + | Args: | ||
| + | data (str): The string to be converted. | ||
| + | |||
| + | Returns: | ||
| + | str: The binary representation of the string, where each character is | ||
| + | encoded as an 8-bit binary number. | ||
| + | |||
| + | Examples: | ||
| + | >>> str_2_bin("Hello") | ||
| + | '0100100001100101011011000110110001101111' | ||
| + | >>> str_2_bin("IC") | ||
| + | '0100100101000011' | ||
| + | """ | ||
| + | return "".join(f"{ord(c):08b}" for c in data) | ||
| + | |||
| + | |||
| + | def bin_2_str(data: str) -> str: | ||
| + | """Converts a binary string to a string. | ||
| + | |||
| + | Args: | ||
| + | data (str): The binary string to be converted. It should have a multiple | ||
| + | of 8 characters and only contain valid binary digits (0 or 1). | ||
| + | |||
| + | Returns: | ||
| + | str: The string representation of the binary string, where each group | ||
| + | of 8 binary digits is decoded as a character. | ||
| + | |||
| + | Examples: | ||
| + | >>> bin_2_str("0100100001100101011011000110110001101111") | ||
| + | 'Hello' | ||
| + | >>> bin_2_str("0100100101000011") | ||
| + | 'IC' | ||
| + | """ | ||
| + | return "".join(chr(int(b, 2)) for b in _chunks(data, 8)) | ||
| + | |||
| + | |||
| + | def str_2_hex(data: str) -> str: | ||
| + | """Converts a string to a hexadecimal representation. | ||
| + | |||
| + | Args: | ||
| + | data (str): The string to be converted. | ||
| + | |||
| + | Returns: | ||
| + | str: The hexadecimal representation of the string, where each character | ||
| + | is encoded as a pair of hexadecimal digits. | ||
| + | |||
| + | Examples: | ||
| + | >>> str_2_hex("Hello") | ||
| + | '48656c6c6f' | ||
| + | >>> str_2_hex("IC") | ||
| + | '4943' | ||
| + | """ | ||
| + | return "".join(f"{ord(c):02x}" for c in data) | ||
| + | |||
| + | |||
| + | def hex_2_str(data: str) -> str: | ||
| + | """Converts a hexadecimal string to a string. | ||
| + | |||
| + | Args: | ||
| + | data (str): The hexadecimal string to be converted. It should have an | ||
| + | even number of characters and only contain valid hexadecimal digits | ||
| + | (0-9, A-F, a-f). | ||
| + | |||
| + | Returns: | ||
| + | str: The string representation of the hexadecimal string, where each | ||
| + | pair of hexadecimal digits is decoded as a character. | ||
| + | |||
| + | Examples: | ||
| + | >>> hex_2_str("48656c6c6f") | ||
| + | 'Hello' | ||
| + | >>> hex_2_str("4943") | ||
| + | 'IC' | ||
| + | """ | ||
| + | return "".join(chr(int(x, 16)) for x in _chunks(data, 2)) | ||
| + | |||
| + | |||
| + | # XOR functions | ||
| + | |||
| + | |||
| + | def strxor(operand_1: str, operand_2: str) -> str: | ||
| + | """Performs a bitwise exclusive OR (XOR) operation on two strings. | ||
| + | |||
| + | Args: | ||
| + | operand_1 (str): The first string to be XORed. | ||
| + | operand_2 (str): The second string to be XORed. | ||
| + | |||
| + | Returns: | ||
| + | str: The result of the XOR operation on the two strings, where each | ||
| + | character is encoded as an 8-bit binary number. The result has | ||
| + | the same length as the shorter input string. | ||
| + | |||
| + | Examples: | ||
| + | >>> strxor("Hello", "IC") | ||
| + | '\\x01&' | ||
| + | >>> strxor("secret", "key") | ||
| + | '\\x18\\x00\\x1a' | ||
| + | """ | ||
| + | return "".join(chr(ord(x) ^ ord(y)) for (x, y) in zip(operand_1, operand_2)) | ||
| + | |||
| + | |||
| + | def bitxor(operand_1: str, operand_2: str) -> str: | ||
| + | """Performs a bitwise exclusive OR (XOR) operation on two bit-strings. | ||
| + | |||
| + | Args: | ||
| + | operand_1 (str): The first bit-string to be XORed. It should only | ||
| + | contain valid binary digits (0 or 1). | ||
| + | operand_2 (str): The second bit-string to be XORed. It should only | ||
| + | contain valid binary digits (0 or 1). | ||
| + | |||
| + | Returns: | ||
| + | str: The result of the XOR operation on the two bit-strings, where each | ||
| + | bit is encoded as a character. The result has the same length as | ||
| + | the shorter input bit-string. | ||
| + | |||
| + | Examples: | ||
| + | >>> bitxor("01001000", "01000010") | ||
| + | '00001010' | ||
| + | >>> bitxor("10101010", "00110011") | ||
| + | '10011001' | ||
| + | """ | ||
| + | return "".join(str(int(x) ^ int(y)) for (x, y) in zip(operand_1, operand_2)) | ||
| + | |||
| + | |||
| + | def hexxor(operand_1: str, operand_2: str) -> str: | ||
| + | """Performs a bitwise exclusive OR (XOR) operation on two hexadecimal | ||
| + | strings. | ||
| + | |||
| + | Args: | ||
| + | operand_1 (str): The first hexadecimal string to be XORed. It should | ||
| + | have an even number of characters and only contain valid hexadecimal | ||
| + | digits (0-9, A-F, a-f). | ||
| + | operand_2 (str): The second hexadecimal string to be XORed. It should | ||
| + | have an even number of characters and only contain valid | ||
| + | digits (0-9, A-F, a-f). | ||
| + | |||
| + | Returns: | ||
| + | str: The result of the XOR operation on the two hexadecimal strings, | ||
| + | where each pair of hexadecimal digits is encoded as a pair of | ||
| + | hexadecimal digits. The result has the same length as the shorter | ||
| + | input hexadecimal string. | ||
| + | |||
| + | Examples: | ||
| + | >>> hexxor("48656c6c6f", "42696e67") | ||
| + | '0a0c020b' | ||
| + | >>> hexxor("736563726574", "6b6579") | ||
| + | '18001a' | ||
| + | """ | ||
| + | return "".join( | ||
| + | _hex(int(x, 16) ^ int(y, 16)) | ||
| + | for (x, y) in zip(_chunks(operand_1, 2), _chunks(operand_2, 2)) | ||
| + | ) | ||
| + | |||
| + | |||
| + | # Python3 'bytes' functions | ||
| + | |||
| + | |||
| + | def bytes_to_string(bytes_data: bytearray | bytes) -> str: | ||
| + | """Converts a byte array or a byte string to a string. | ||
| + | |||
| + | Args: | ||
| + | bytes_data (bytearray | bytes): The byte array or the byte string to be | ||
| + | converted. It should be encoded in Latin-1 format. | ||
| + | |||
| + | Returns: | ||
| + | str: The string representation of the byte array or the byte string, | ||
| + | decoded using Latin-1 encoding. | ||
| + | |||
| + | Examples: | ||
| + | >>> bytes_to_string(b'Hello') | ||
| + | 'Hello' | ||
| + | >>> bytes_to_string(bytearray(b'IC')) | ||
| + | 'IC' | ||
| + | """ | ||
| + | return bytes_data.decode(encoding="raw_unicode_escape") | ||
| + | |||
| + | |||
| + | def string_to_bytes(string_data: str) -> bytes: | ||
| + | """Converts a string to a byte string. | ||
| + | |||
| + | Args: | ||
| + | string_data (str): The string to be converted. | ||
| + | |||
| + | Returns: | ||
| + | bytes: The byte string representation of the string, encoded using | ||
| + | Latin-1 encoding. | ||
| + | |||
| + | Examples: | ||
| + | >>> string_to_bytes('Hello') | ||
| + | b'Hello' | ||
| + | >>> string_to_bytes('IC') | ||
| + | b'IC' | ||
| + | """ | ||
| + | return string_data.encode(encoding="raw_unicode_escape") | ||
| + | |||
| + | |||
| + | # Base64 functions | ||
| + | |||
| + | |||
| + | def b64encode(data: str) -> str: | ||
| + | """Encodes a string to base64. | ||
| + | |||
| + | Parameters: | ||
| + | data (str): The string to be encoded. | ||
| + | |||
| + | Returns: | ||
| + | str: The base64 encoded string, using Latin-1 encoding. | ||
| + | |||
| + | Examples: | ||
| + | >>> b64encode("Hello") | ||
| + | 'SGVsbG8=' | ||
| + | >>> b64encode("IC") | ||
| + | 'SUM=' | ||
| + | """ | ||
| return bytes_to_string(base64.b64encode(string_to_bytes(data))) | return bytes_to_string(base64.b64encode(string_to_bytes(data))) | ||
| - | # PYTHON3 'BYTES' FUNCTIONS | ||
| - | def bytes_to_string(bytes_data): | ||
| - | return bytes_data.decode() # default utf-8 | ||
| - | def string_to_bytes(string_data): | + | def b64decode(data: str) -> str: |
| - | return string_data.encode() # default utf-8 | + | """Decodes a base64 encoded string. |
| + | |||
| + | Args: | ||
| + | data (str): The base64 encoded string to be decoded. It should only | ||
| + | contain valid base64 characters (A-Z, a-z, 0-9, +, /, =). | ||
| + | |||
| + | Returns: | ||
| + | str: The decoded string, using Latin-1 encoding. | ||
| + | |||
| + | Examples: | ||
| + | >>> b64decode("SGVsbG8=") | ||
| + | 'Hello' | ||
| + | >>> b64decode("SUM=") | ||
| + | 'IC' | ||
| + | """ | ||
| + | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | ||
| </file> | </file> | ||
| </spoiler> | </spoiler> | ||
| Line 168: | Line 434: | ||
| - | def permute4_32(s: str) -> str: | ||
| - | """Perform a permutatation by shifting all bits 4 positions right. | ||
| - | |||
| - | Args: | ||
| - | s (str): The input is assumed to be a 32-bit bitstring. | ||
| - | |||
| - | Raises: | ||
| - | TypeError: Expected input to be a string. | ||
| - | ValueError: Expected input to have length 32. | ||
| - | |||
| - | Returns: | ||
| - | str: The permuted string by shifting all bits 4 positions right. | ||
| - | """ | ||
| - | if not isinstance(s, str): | ||
| - | raise TypeError( | ||
| - | f"Invalid type for the input. Expected `str`, but got `{type(s)}`." | ||
| - | ) | ||
| - | if len(s) != 32: | ||
| - | raise ValueError( | ||
| - | f"Invalit input length. Expected to be 32, but got {len(s)}." | ||
| - | ) | ||
| - | return s[28:32] + s[0:28] | ||
| - | |||
| - | |||
| - | def permute_inv4_32(s: str) -> str: | ||
| - | """Perform the inverse of permute4_32. | ||
| - | |||
| - | Args: | ||
| - | s (str): The input is assumed to be a 32-bit bitstring. | ||
| - | |||
| - | Raises: | ||
| - | TypeError: Expected input to be a string. | ||
| - | ValueError: Expected input to have length 32. | ||
| - | |||
| - | Returns: | ||
| - | str: The permuted string by shifting all bits 4 positions left. | ||
| - | """ | ||
| - | if not isinstance(s, str): | ||
| - | raise TypeError( | ||
| - | f"Invalid type for the input. Expected `str`, but got `{type(s)}`." | ||
| - | ) | ||
| - | if len(s) != 32: | ||
| - | raise ValueError( | ||
| - | f"Invalit input length. Expected to be 32, but got {len(s)}." | ||
| - | ) | ||
| - | return s[4:32] + s[0:4] | ||
| Line 332: | Line 552: | ||
| return y1 + y2 | return y1 + y2 | ||
| + | |||
| + | |||
| Line 338: | Line 560: | ||
| msg = "Hi" | msg = "Hi" | ||
| key = "??" # Find the key that should replace ?? | key = "??" # Find the key that should replace ?? | ||
| + | ks = str_2_bin(key) | ||
| + | |||
| + | # TODO 1: Find the key in the reduced 2-byte SPN | ||
| + | |||
| xs = str_2_bin(msg) | xs = str_2_bin(msg) | ||
| - | ks = str_2_bin(key) | ||
| ys = spn_1r_reduced_2s(ks, xs) | ys = spn_1r_reduced_2s(ks, xs) | ||
| print( | print( | ||
| - | f"Two y halves of reduced SPN:" | + | "Two y halves of reduced SPN:" |
| f" {ys[0:8]} (hex: {bin_2_hex(ys[0:8])})," | f" {ys[0:8]} (hex: {bin_2_hex(ys[0:8])})," | ||
| f" {ys[8:16]} (hex: {bin_2_hex(ys[8:16])})" | f" {ys[8:16]} (hex: {bin_2_hex(ys[8:16])})" | ||
| Line 351: | Line 576: | ||
| msg = "Om" | msg = "Om" | ||
| key = "????" # Find the key that should replace ???? | key = "????" # Find the key that should replace ???? | ||
| - | xs = str_2_bin(msg) | ||
| ks = str_2_bin(key) | ks = str_2_bin(key) | ||
| + | |||
| + | # TODO 2: Find the key in the full 2-byte SPN | ||
| + | |||
| + | xs = str_2_bin(msg) | ||
| ys = spn_1r_full_2s(ks, xs) | ys = spn_1r_full_2s(ks, xs) | ||
| print( | print( | ||
| - | f"Two y halves of full SPN (2 bytes):" | + | "Two y halves of full SPN (2 bytes):" |
| f" {ys[0:8]} (hex: {bin_2_hex(ys[0:8])})," | f" {ys[0:8]} (hex: {bin_2_hex(ys[0:8])})," | ||
| f" {ys[8:16]} (hex: {bin_2_hex(ys[8:16])})" | f" {ys[8:16]} (hex: {bin_2_hex(ys[8:16])})" | ||
| ) | ) | ||
| + | |||
| if __name__ == "__main__": | if __name__ == "__main__": | ||
| - | main() | + | main() |
| </code> | </code> | ||
| </spoiler> | </spoiler> | ||
| Line 390: | Line 619: | ||
| De data aceasta nu se poate executa o cautare brute-force pe toți octeții de XOR. Încercați să atacați câte un S-box pe rând. Gandiți-vă ce biți sunt afectați de fiecare S-box pentru a construi un atac eficient. Ce se întâmplă dacă faceți brute force pe k5 și k6? Puteți să aflați k1? | De data aceasta nu se poate executa o cautare brute-force pe toți octeții de XOR. Încercați să atacați câte un S-box pe rând. Gandiți-vă ce biți sunt afectați de fiecare S-box pentru a construi un atac eficient. Ce se întâmplă dacă faceți brute force pe k5 și k6? Puteți să aflați k1? | ||
| </note> | </note> | ||
| + | </hidden> | ||