Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ic:labs:06 [2021/11/07 22:18]
philip.dumitru
ic:labs:06 [2023/10/09 23:22] (current)
razvan.smadu
Line 5: Line 5:
 Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://​drive.google.com/​file/​d/​1YFkD5I6yNgVf1y9bhK4xdR2xZgDZ4Dd1/​view?​usp=sharing|aici]]. Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://​drive.google.com/​file/​d/​1YFkD5I6yNgVf1y9bhK4xdR2xZgDZ4Dd1/​view?​usp=sharing|aici]].
  
 +Puteți lucra acest laborator folosind platforma Google Colab, accesând acest [[https://​colab.research.google.com/​github/​ACS-IC-labs/​IC-labs/​blob/​main/​labs/​lab06/​lab6.ipynb|link]].
 +
 +
 +<​hidden>​
 +<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]+
  
-def byte_2_bin(bval):​ + 
-    """​ +def _pad(data: str, size: int) -> str: 
-      Transform ​a byte (8-bit) value into bitstring+    reminder = len(data) % size 
 +    if reminder != 0: 
 +        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 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: str-> str
-  +    ​"""​Converts a hexadecimal string to a binary representation. 
-def hex_2_bin(data): + 
-    ​return ​''​.join(f'{int(x, 16):08b}' ​for x in _chunks(data,​ 2)) +    Args: 
-  +        data (str): The hexadecimal string to be converted. It should have an 
-def str_2_bin(data): +            even number of characters and only contain valid hexadecimal digits 
-    ​return ''​.join(f'{ord(c):08b}' for c in data+            (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): + 
-    ​return ​''​.join(f'{ord(c):02x}' ​for c in data) +    ​Examples: 
-  +        >>>​ hex_2_bin("​01abcd"​) 
-def bin_2_str(data):​ +        ​'000000011010101111001101' 
-    ​return ​''​.join(chr(int(b,​ 2)) for b in _chunks(data,​ 8)) +        >>>​ hex_2_bin("​0a"​) 
-  +        '​00001010'​ 
-def hex_2_str(data): +    """​ 
-    return ''​.join(chr(int(x,​ 16)) for x in _chunks(data,​ 2)) +    return ""​.join(f"{int(x, 16):08b}" ​for x in _chunks(data,​ 2)) 
-  + 
-# XOR FUNCTIONS + 
-def strxor(ab):  # xor two strings, ​trims the longer ​input +def bin_2_hex(data: str-> str
-    ​return ​''​.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(ab)) +    ​"""​Converts a binary string to a hexadecimal representation. 
-  + 
-def bitxor(ab):  # xor two bit-strings, ​trims the longer ​input +    Args: 
-    ​return ​''​.join(str(int(x) ^ int(y)) for (x, y) in zip(ab)) +        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)
-def hexxor(ab):  # xor two hex-strings, ​trims the longer ​input + 
-    ​return ​''​.join(_hex(int(x,​ 16) ^ int(y, 16)) for (x, y) in zip(_chunks(a, 2), _chunks(b, 2))) +    Returns: 
-  +        str: The hexadecimal representation of the binary string, where each 
-BASE64 FUNCTIONS +            group of 8 binary digits is encoded as a pair of hexadecimal digits. 
-def b64decode(data): + 
-    ​return ​bytes_to_string(base64.b64decode(string_to_bytes(data))) +    Examples: 
-  +        >>>​ bin_2_hex("​000000011010101111001101"​
-def b64encode(data):​+        '​01abcd'​ 
 +        >>> ​bin_2_hex("​00001010"​
 +        '0a' 
 +    """​ 
 +    return ""​.join(f"{int(b, 2):02x}" ​for b in _chunks(data,​ 8)) 
 + 
 + 
 +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: stroperand_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_1operand_2)) 
 + 
 + 
 +def bitxor(operand_1: stroperand_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_1operand_2)) 
 + 
 + 
 +def hexxor(operand_1: stroperand_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): +def b64decode(data: str-> str
-    ​return bytes_data.decode()  # default utf-8 +    ​"""​Decodes a base64 encoded string. 
-  + 
-def string_to_bytes(string_data): +    Args: 
-    return ​string_data.encode()  # default utf-8+        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>​
  
 ==== AES ECB ==== ==== AES ECB ====
Line 83: Line 355:
 Pentru următoarele exerciții vom folosi {{:​ic:​laboratoare:​aesecb_Attack.zip|scheletul de laborator}}. Pentru următoarele exerciții vom folosi {{:​ic:​laboratoare:​aesecb_Attack.zip|scheletul de laborator}}.
  
-==== Exercițiul 1 - Determinarea dimensiunii blocului ====+==== Exercițiul 1 - Determinarea dimensiunii blocului ​(3p) ====
 Primul pas necesar, într-un atac asupra unei criptări pe blocuri, este determinarea dimensiunii unui bloc. Deși cunoaștem deja dimensiunea în acest caz, este un pas necesar în alte situații. Primul pas necesar, într-un atac asupra unei criptări pe blocuri, este determinarea dimensiunii unui bloc. Deși cunoaștem deja dimensiunea în acest caz, este un pas necesar în alte situații.
  
Line 89: Line 361:
 <note tip> <note tip>
  
-Funcția **findBlockSize()** mărește numărul de caractere adăugate, mărind lungimea mesajului.+Funcția **find_block_size()** mărește numărul de caractere adăugate, mărind lungimea mesajului.
  
 Cum putem asocia dimensiunea mesajului cu numărul de blocuri criptate? Cum putem asocia dimensiunea mesajului cu numărul de blocuri criptate?
 </​note>​ </​note>​
  
-==== Exercițiul 2 - Determinarea dimensiunii prefixului ====+==== Exercițiul 2 - Determinarea dimensiunii prefixului ​(3p) ====
 Vom aborda această problemă la fel ca în pasul anterior. Trimitem mesaje din ce în ce mai mari. Atunci când blocul criptat corespunzător prefixului nu se va mai schimba, putem calcula lungimea sa. Vom aborda această problemă la fel ca în pasul anterior. Trimitem mesaje din ce în ce mai mari. Atunci când blocul criptat corespunzător prefixului nu se va mai schimba, putem calcula lungimea sa.
 <​code>​ <​code>​
Line 115: Line 387:
 </​note>​ </​note>​
  
-==== Exercițiul 3 - ECB Byte at a Time Attack ====+==== Exercițiul 3 - ECB Byte at a Time Attack ​(4p) ====
 Presupunem că avem un algoritm de criptare-bloc care criptează 16 bytes, producând ciphertext de 16 bytes. Folosim acest algoritm pentru a cripta 2 blocuri de date necunoscute,​ m1 și m2. În plus, avem voie să trimitem propriul input m0, care va fi lipit în fața acestor blocuri. Presupunem că avem un algoritm de criptare-bloc care criptează 16 bytes, producând ciphertext de 16 bytes. Folosim acest algoritm pentru a cripta 2 blocuri de date necunoscute,​ m1 și m2. În plus, avem voie să trimitem propriul input m0, care va fi lipit în fața acestor blocuri.
 Pentru că putem trimite orice mesaj, vom alege să trimitem unul de lungime 16. Astfel, în cazul în care se folosește modul ECB, putem afla Enc(m0). Având acces la perechi de input propriu - ciphertext poate fi foarte util în acest caz, având de fapt un oracol. În cazul în care am trimite doar 15 bytes, putem afla ultimul byte prin brute force. Pentru că putem trimite orice mesaj, vom alege să trimitem unul de lungime 16. Astfel, în cazul în care se folosește modul ECB, putem afla Enc(m0). Având acces la perechi de input propriu - ciphertext poate fi foarte util în acest caz, având de fapt un oracol. În cazul în care am trimite doar 15 bytes, putem afla ultimul byte prin brute force.
Line 147: Line 419:
 ==== Lab Code ==== ==== Lab Code ====
  
 +<spoiler Click pentru a vedea lab06.py>​
 <file python lab06.py>​ <file python lab06.py>​
-from math import ceil 
 import base64 import base64
-import os +from math import ​ceil 
-from random ​import ​randint +from typing ​import ​List, Tuple 
-from Crypto.Cipher ​import ​AES +
-from utils import * +
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes+
 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
 +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 +from utils import *
 +
 backend = default_backend() backend = default_backend()
  
  
-def split_bytes_in_blocks(x,​ block_size):​ +def split_bytes_in_blocks(x: bytes, block_size: int-> List[bytes]
-    ​nb_blocks = ceil(len(x)/​block_size) +    ​"""​Splits a byte string into a list of blocks of equal size.
-    return [x[block_size*i:​block_size*(i+1)] for i in range(nb_blocks)]+
  
 +    Args:
 +        x (bytes): The byte string to split.
 +        block_size (int): The size of each block in bytes.
  
-def pkcs7_padding(message,​ block_size):​+    Returns: 
 +        List[bytes]:​ A list of byte strings, each of length block_size,​ 
 +            except for the last one which may be shorter. 
 +    """​ 
 +    nb_blocks = ceil(len(x) / block_size) 
 +    return [x[block_size * i : block_size * (i + 1)] for i in range(nb_blocks)] 
 + 
 + 
 +def pkcs7_padding(message: bytes, block_size: int-> bytes: 
 +    """​Applies PKCS#7 padding to a byte string. 
 + 
 +    Args: 
 +        message (bytes): The byte string to pad. 
 +        block_size (int): The size of the block in bytes. 
 + 
 +    Returns: 
 +        bytes: A byte string that is a multiple of block_size in length, 
 +            with padding bytes added at the end. The value of each padding 
 +            byte is equal to the number of padding bytes added. 
 +    """​
     padding_length = block_size - (len(message) % block_size)     padding_length = block_size - (len(message) % block_size)
     if padding_length == 0:     if padding_length == 0:
Line 172: Line 466:
  
  
-def pkcs7_strip(data):​+def pkcs7_strip(data: bytes-> bytes: 
 +    """​Removes PKCS#7 padding from a byte string. 
 + 
 +    Args: 
 +        data (bytes): The byte string to strip. 
 + 
 +    Returns: 
 +        bytesA byte string with the padding bytes removed from the end. 
 +    """​
     padding_length = data[-1]     padding_length = data[-1]
-    return data[:- padding_length]+    return data[:​-padding_length]
  
  
-def encrypt_aes_128_ecb(msg, key): +def encrypt_aes_128_ecb(plaintext: bytes, key: bytes-> bytes: 
-    padded_msg = pkcs7_padding(msg, block_size=16)+    """​Encrypts a byte string using AES-128 in ECB mode. 
 + 
 +    Args: 
 +        plaintext (bytes): The byte string to encrypt. It will be padded 
 +            using PKCS#7. 
 +        key (bytes): The encryption key. It must be 16 bytes in length. 
 + 
 +    Returns: 
 +        bytes: A byte string that is the encrypted version of plaintext. 
 +    """​ 
 +    padded_msg = pkcs7_padding(plaintext, block_size=16)
     cipher = Cipher(algorithms.AES(key),​ modes.ECB(),​ backend=backend)     cipher = Cipher(algorithms.AES(key),​ modes.ECB(),​ backend=backend)
     encryptor = cipher.encryptor()     encryptor = cipher.encryptor()
Line 184: Line 496:
  
  
-def decrypt_aes_128_ecb(ctxt, key):+def decrypt_aes_128_ecb(ciphertext: bytes, key: bytes-> bytes: 
 +    """​Decrypts a byte string using AES-128 in ECB mode. 
 + 
 +    Args: 
 +        ciphertext (bytes): The byte string to decrypt. It must be a multiple of 
 +            16 bytes in length. 
 +        key (bytes): The decryption key. It must be 16 bytes in length. 
 + 
 +    Returns: 
 +        bytesA byte string that is the decrypted version of ciphertext. The 
 +            PKCS#7 padding will be removed. 
 +    """​
     cipher = Cipher(algorithms.AES(key),​ modes.ECB(),​ backend=backend)     cipher = Cipher(algorithms.AES(key),​ modes.ECB(),​ backend=backend)
     decryptor = cipher.decryptor()     decryptor = cipher.decryptor()
-    decrypted_data = decryptor.update(ctxt) + decryptor.finalize()+    decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
     message = pkcs7_strip(decrypted_data)     message = pkcs7_strip(decrypted_data)
     return message     return message
  
-# You are not suppose to see this+
 class Oracle: class Oracle:
-    def __init__(self):​ +    ​"""​A class that simulates an encryption oracle using AES-128 in ECB mode. 
-        self.key = 'Mambo NumberFive'.encode() +    You are not suppose to see this"""​ 
-        self.prefix = 'PREF'.encode() + 
-        self.target = base64.b64decode( ​ # You are suppose to break this +    ​def __init__(self) ​-> None
-            "RG8gbm90IGxheSB1cCBmb3IgeW91cnNlbHZlcyB0cmVhc3VyZXMgb24gZWFydGgsIHdoZXJlIG1vdGggYW5kIHJ1c3QgZGVzdHJveSBhbmQgd2hlcmUgdGhpZXZlcyBicmVhayBpbiBhbmQgc3RlYWwsCmJ1dCBsYXkgdXAgZm9yIHlvdXJzZWx2ZXMgdHJlYXN1cmVzIGluIGhlYXZlbiwgd2hlcmUgbmVpdGhlciBtb3RoIG5vciBydXN0IGRlc3Ryb3lzIGFuZCB3aGVyZSB0aGlldmVzIGRvIG5vdCBicmVhayBpbiBhbmQgc3RlYWwuCkZvciB3aGVyZSB5b3VyIHRyZWFzdXJlIGlzLCB0aGVyZSB5b3VyIGhlYXJ0IHdpbGwgYmUgYWxzby4="+        self.key = "Mambo NumberFive".encode() 
 +        self.prefix = "PREF".encode() 
 + 
 +        # You are suppose to break this 
 +        self.target = base64.b64decode( 
 +            "RG8gbm90IGxheSB1cCBmb3IgeW91cnNlbHZlcyB0cmVhc3VyZXMgb24gZWFydGgsI"​ 
 +            "​HdoZXJlIG1vdGggYW5kIHJ1c3QgZGVzdHJveSBhbmQgd2hlcmUgdGhpZXZlcyBicm"​ 
 +            "​VhayBpbiBhbmQgc3RlYWwsCmJ1dCBsYXkgdXAgZm9yIHlvdXJzZWx2ZXMgdHJlYXN"​ 
 +            "​1cmVzIGluIGhlYXZlbiwgd2hlcmUgbmVpdGhlciBtb3RoIG5vciBydXN0IGRlc3Ry"​ 
 +            "​b3lzIGFuZCB3aGVyZSB0aGlldmVzIGRvIG5vdCBicmVhayBpbiBhbmQgc3RlYWwuC"​ 
 +            "​kZvciB3aGVyZSB5b3VyIHRyZWFzdXJlIGlzLCB0aGVyZSB5b3VyIGhlYXJ0IHdpbG"​ 
 +            "​wgYmUgYWxzby4="
         )         )
  
-    def encrypt(self,​ message):+    def encrypt(self,​ message: bytes-> bytes:
         return encrypt_aes_128_ecb(         return encrypt_aes_128_ecb(
             self.prefix + message + self.target,​             self.prefix + message + self.target,​
-            self.key+            self.key,
         )         )
 +
  
 # Task 1 # Task 1
-def findBlockSize(): +def find_block_size() -> Tuple[int, int, int]
-    ​initialLength ​= len(Oracle().encrypt(b''​))+    ​initial_length ​= len(Oracle().encrypt(b""​))
     i = 0     i = 0
-    ​while 1:  # Feed identical bytes of your-string to the function 1 at a time until you get the block length + 
-        # You will also need to determine here the size of fixed prefix + target + pad +    block_size = 0 
-        # And the minimum ​size of the plaintext to make a new block +    size_of_prefix_target_padding = 0 
-        length = len(Oracle().encrypt(b'X'*i))+    minimum_size_to_align_plaintext = 0 
 + 
 +    ​while 1: 
 +        ​# Feed identical bytes of your-string to the function 1 at a time 
 +        # until you get the block lengthYou will also need to determine 
 +        # here the size of fixed prefix + target + pad, and the minimum 
 +        # size of the plaintext to make a new block 
 +        length = len(Oracle().encrypt(b"X" ​* i))
         i += 1         i += 1
  
-        return block_size, ​sizeOfTheFixedPrefixPlusTargetminimumSizeToAlighPlaintext+        ​# TODO 1: find block_size, size_of_prefix_target_padding,​ 
 +        # and minimum_size_to_align_plaintext 
 +        break 
 + 
 +    ​return ​
 +        ​block_size, 
 +        size_of_prefix_target_padding,​ 
 +        minimum_size_to_align_plaintext, 
 +    ) 
  
 # Task 2 # Task 2
-def findPrefixSize(block_size):​ +def find_prefix_size(block_size: int-> int
-    ​previous_blocks ​None +    ​initial_blocks ​= split_bytes_in_blocks(Oracle().encrypt(b""​), ​block_size)
-    # Find the situation where prefix_size + padding_size - 1 = block_size +
-    # Use split_bytes_in_blocks ​to get blocks of size(block_size)+
  
 +    # TODO 2: Find when prefix_size + padding_size - 1 = block_size
 +    # Use split_bytes_in_blocks to get blocks of size block_size.
 +
 +    # TODO 2.1: Find the block containing the prefix by comparing
 +    # initial_blocks and modified_blocks
 +    # You may find enumerate() and zip() useful.
 +    modified_blocks = split_bytes_in_blocks(Oracle().encrypt(b"​X"​),​ block_size)
 +    prefix_block_index = 0
 +
 +    # TODO 2.2: As now we know in which block to look, find when that block
 +    # does not change anymore when adding more X's. The complementary will
 +    # represent the prefix.
 +    prefix_size_in_block = 0
 +
 +    prefix_size = prefix_block_index * block_size + prefix_size_in_block
     return prefix_size     return prefix_size
  
  
 # Task 3 # Task 3
-def recoverOneByteAtATime(block_size,​ prefix_size,​ target_size):​+def recover_one_byte_at_a_time( 
 +    ​block_size: int, 
 +    ​prefix_size: int, 
 +    ​target_size: int, 
 +-> str:
     known_target_bytes = b""​     known_target_bytes = b""​
 +
     for _ in range(target_size):​     for _ in range(target_size):​
         # prefix_size + padding_length + known_len + 1 = 0 mod block_size         # prefix_size + padding_length + known_len + 1 = 0 mod block_size
-        known_len = len(know_target_bytes)+        known_len = len(known_target_bytes)
  
-        padding_length = (- known_len - 1 - prefix_size) % block_size+        padding_length = (-known_len - 1 - prefix_size) % block_size
         padding = b"​X"​ * padding_length         padding = b"​X"​ * padding_length
  
-        # target block plaintext ​contains only known characters except its last character +        # TODO 3.1: Determine the target block index which contains only known 
-        # Don't forget to use split_bytes_in_blocks ​to get the correct ​block+        # characters except its last character
 + 
 +        # TODO 3.2: Get the target block form split_bytes_in_blocks ​at the index 
 +        # previously determined. 
 + 
 +        # TODO 3.3: Try every possibility for the last character and search for 
 +        # the block that you already know. That character will be added to 
 +        # the known target bytes. 
 + 
 +    return known_target_bytes.decode() 
  
-        ​trying every possibility for the last character+def main() -> None: 
 +    ​Find block size, prefix size, and length of plaintext size to align blocks 
 +    ( 
 +        block_size,​ 
 +        size_of_prefix_target_padding,​ 
 +        minimum_size_to_align_plaintext,​ 
 +    ) = find_block_size()
  
-    print(known_target_bytes.decode())+    print(f"​Block size:​\t\t\t\t{block_size}"​) 
 +    print( 
 +        "Size of prefix, target, and padding:"​ 
 +        f"​\t{size_of_prefix_target_padding}"​ 
 +    ​) 
 +    print(f"​Pad needed to align:​\t\t\t{minimum_size_to_align_plaintext}"​)
  
 +    # Find size of the prefix
 +    prefix_size = find_prefix_size(block_size)
 +    print(f"​\nPrefix Size:​\t{prefix_size}"​)
  
-Find block size, prefix size, and length ​of plaintext size to allign blocks +    ​Size of the target 
-block_size, sizeOfTheFixedPrefixPlusTarget,​ minimumSizeToAlignPlaintext ​findBlockSize() +    ​target_size ​= ( 
-print("​Block size:​\t\t\t"​ + str(block_size)) +        ​size_of_prefix_target_padding 
-print("​Size of prefix and target:​\t"​ + str(sizeOfTheFixedPrefixPlusTarget)) +        - minimum_size_to_align_plaintext 
-print("​Pad needed to align:​\t\t"​ + str(minimumSizeToAlignPlaintext))+        - prefix_size 
 +    ​)
  
-Find size of the prefix +    ​Recover ​the target 
-prefix_size ​findPrefixSize(block_size) +    ​recovered_target ​recover_one_byte_at_a_time( 
-print("​\nPrefix Size:\t" ​+ str(prefix_size))+        ​block_size
 +        prefix_size,​ 
 +        target_size,​ 
 +    ​
 +    print(f"\nTarget{recovered_target}")
  
-# Size of the target 
-target_size = sizeOfTheFixedPrefixPlusTarget - \ 
-    minimumSizeToAlignPlaintext - prefix_size 
  
-print("\nTarget:") +if __name__ == "​__main__": 
-recoverOneByteAtATime(block_size, prefix_size,​ target_size)+    main()
 </​file>​ </​file>​
 +</​spoiler>​
 +</​hidden>​
ic/labs/06.1636316296.txt.gz · Last modified: 2021/11/07 22:18 by philip.dumitru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0