Differences

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

Link to this comparison view

ic:labs:06 [2023/10/01 23:31]
razvan.smadu [Exercițiul 1 - Determinarea dimensiunii blocului (3p)]
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 ​și platforma Google Colab, accesând acest [[https://​colab.research.google.com/​drive/1RE4vQdzyOUpC-fuE9bTQUM4-tW4anc7A?​usp=sharing|link]].+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>​ <spoiler Click pentru a vedea utils.py>​
 <file python utils.py>​ <file python utils.py>​
Line 418: 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 443: 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 455: 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, ​sizeOfPrefixTargetPaddingminimumSizeToAlighPlaintext+        ​# 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(known_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.
  
-        # trying every possibility for the last character+        # TODO 3.2: Get the target block form split_bytes_in_blocks at the index 
 +        # previously determined.
  
-    print(known_target_bytes.decode())+        # 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()
  
-# Find block size, prefix size, and length of plaintext size to allign blocks 
-block_size, sizeOfPrefixTargetPadding,​ minimumSizeToAlignPlaintext = findBlockSize() 
-print("​Block size:​\t\t\t\t"​ + str(block_size)) 
-print("​Size of prefix, target, and padding:​\t"​ + str(sizeOfPrefixTargetPadding)) 
-print("​Pad needed to align:​\t\t\t"​ + str(minimumSizeToAlignPlaintext)) 
  
-# Find size of the prefix +def main() -> None: 
-prefix_size = findPrefixSize(block_size) +    ​# Find block sizeprefix ​size, and length of plaintext size to align blocks 
-print("​\nPrefix Size:​\t"​ + str(prefix_size))+    ( 
 +        ​block_size, 
 +        ​size_of_prefix_target_padding,​ 
 +        minimum_size_to_align_plaintext,​ 
 +    ​= find_block_size()
  
-Size of the target +    print(f"​Block size:​\t\t\t\t{block_size}"​) 
-target_size = sizeOfPrefixTargetPadding - +    print( 
-    ​minimumSizeToAlignPlaintext - prefix_size+        "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}"​)
  
-print("​\nTarget:") +    # Find size of the prefix 
-recoverOneByteAtATime(block_size,​ prefix_size,​ target_size)+    prefix_size = find_prefix_size(block_size) 
 +    ​print(f"\nPrefix Size:\t{prefix_size}") 
 + 
 +    # Size of the target 
 +    target_size = ( 
 +        size_of_prefix_target_padding 
 +        - minimum_size_to_align_plaintext 
 +        - prefix_size 
 +    ) 
 + 
 +    # Recover the target 
 +    recovered_target = recover_one_byte_at_a_time( 
 +        ​block_size, 
 +        ​prefix_size,​ 
 +        ​target_size
 +    ) 
 +    print(f"​\nTarget:​ {recovered_target}"​) 
 + 
 + 
 +if __name__ == "​__main__":​ 
 +    main()
 </​file>​ </​file>​
 +</​spoiler>​
 +</​hidden>​
ic/labs/06.1696192280.txt.gz · Last modified: 2023/10/01 23:31 by razvan.smadu
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