This shows you the differences between two versions of the page.
ic:labs:05 [2021/11/01 15:51] razvan.smadu [Exercitiul 3 (5p)] |
ic:labs:05 [2023/10/09 23:22] (current) razvan.smadu |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Laboratorul 05 - DES ===== | ===== Laboratorul 05 - DES ===== | ||
- | In acest laborator vom face niste exercitii ce folosesc algoritmul DES si variatii ale acestuia discutate la [[https://drive.google.com/file/d/1Fjybv6k5QudRB1bkAi5shVUGlUyTO_0U/view|curs]]. | + | În acest laborator vom face niște exerciții ce folosesc algoritmul DES și variații ale acestuia discutate la [[https://drive.google.com/file/d/1Fjybv6k5QudRB1bkAi5shVUGlUyTO_0U/view|curs]]. |
Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1FU422fCHM24fRnMuzFd0OjhQqT6AQZ3X/view?usp=sharing|aici]]. | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1FU422fCHM24fRnMuzFd0OjhQqT6AQZ3X/view?usp=sharing|aici]]. | ||
+ | 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/lab05/lab5.ipynb|acest]] 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): | + | def _pad(data: str, size: int) -> str: |
- | yield string[i:i+chunk_size] | + | reminder = len(data) % size |
- | + | if reminder != 0: | |
- | def byte_2_bin(bval): | + | data = "0" * (size - reminder) + data |
- | """ | + | return data |
- | Transform a byte (8-bit) value into a bitstring | + | |
+ | |||
+ | 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: 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(a, b): # 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(a, b)) | + | """Converts a binary string to a hexadecimal representation. |
- | + | ||
- | def bitxor(a, b): # xor two bit-strings, trims the longer input | + | Args: |
- | return ''.join(str(int(x) ^ int(y)) for (x, y) in zip(a, b)) | + | 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(a, b): # 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: 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 UTF-8 format. | ||
+ | |||
+ | Returns: | ||
+ | str: The string representation of the byte array or the byte string, | ||
+ | decoded using UTF-8 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> bytes_to_string(b'Hello') | ||
+ | 'Hello' | ||
+ | >>> bytes_to_string(bytearray(b'IC')) | ||
+ | 'IC' | ||
+ | """ | ||
+ | return bytes_data.decode(encoding="utf-8") | ||
+ | |||
+ | |||
+ | 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 | ||
+ | UTF-8 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> string_to_bytes('Hello') | ||
+ | b'Hello' | ||
+ | >>> string_to_bytes('IC') | ||
+ | b'IC' | ||
+ | """ | ||
+ | return string_data.encode(encoding="utf-8") | ||
+ | |||
+ | |||
+ | # 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 UTF-8 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): | ||
- | return string_data.encode() # default utf-8 | ||
- | </file> | ||
- | ==== Exercitiul 1 (2p) ==== | + | |
+ | def b64decode(data: str) -> str: | ||
+ | """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 UTF-8 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> b64decode("SGVsbG8=") | ||
+ | 'Hello' | ||
+ | >>> b64decode("SUM=") | ||
+ | 'IC' | ||
+ | """ | ||
+ | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | ||
+ | </file> | ||
+ | </spoiler> | ||
+ | ==== Exercițiul 1 (2p) ==== | ||
DESX este definit sub forma DESX( (k1,k2,k3), m) = k1 ⊕ DES(k2, m ⊕ k3). | DESX este definit sub forma DESX( (k1,k2,k3), m) = k1 ⊕ DES(k2, m ⊕ k3). | ||
- | Construiti un atac impotriva DESX care sa ruleze intr-un timp proportional cu 2<sup>120</sup>. | + | Construiți un atac împotriva DESX care să ruleze într-un timp proporțional cu 2<sup>120</sup>. |
- | ==== Exercise 2 (3p) ==== | + | ==== Exercițiul 2 (3p) ==== |
- | De ce urmatoarele scheme nu aduc nicio imbunatatire fata de DES? Justificati! | + | De ce următoarele scheme nu aduc nicio îmbunătățire față de DES? Justificați! |
* a) c = k1 ⊕ DES(k2, m) | * a) c = k1 ⊕ DES(k2, m) | ||
* b) c = DES(k2, m ⊕ k1) | * b) c = DES(k2, m ⊕ k1) | ||
- | <note tip>Puteti folosi mai multe perechi (mesaj, ciphertext).</note> | + | <note tip>Puteți folosi mai multe perechi (mesaj, ciphertext).</note> |
- | ==== Exercitiul 3 (5p) ==== | + | |
- | Scopul acestui exercitiu este de a implementa atacul meet-in-the-middle pe 2DES. | + | ==== Exercițiul 3 (5p) ==== |
- | Porniti de la scheletul de laborator (vezi mai jos) care se foloseste de biblioteca pycrypto. | + | |
- | Realizati urmatoarele sarcini: | + | Scopul acestui exercițiu este de a implementa atacul meet-in-the-middle pe 2DES. |
+ | Porniți de la scheletul de laborator (vezi mai jos) care se folosește de biblioteca pycrypto. | ||
- | === A. Instalati biblioteca pycrypto === | + | Realizați următoarele sarcini: |
+ | |||
+ | === A. Instalați biblioteca pycrypto === | ||
Vezi https://pypi.org/project/pycryptodome/ | Vezi https://pypi.org/project/pycryptodome/ | ||
- | === B. Implementati 2DES === | + | === B. Implementați 2DES (1p) === |
- | Pornind de la schelet, implementati metodele des2_enc si des2_dec folosind double-DES (2DES). | + | Pornind de la schelet, implementați metodele des2_enc si des2_dec folosind double-DES (2DES). |
2DES( (k1,k2), m) = DES(k1, DES(k2, m)) | 2DES( (k1,k2), m) = DES(k1, DES(k2, m)) | ||
- | === C. Testati 2DES === | + | === C. Testați 2DES (1p) === |
- | Folositi urmatoarele ciphertexts | + | Folosiți următoarele ciphertexts |
c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | ||
Line 103: | Line 373: | ||
c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' | c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' | ||
- | Decriptati-le folosind cheile: | + | Decriptați-le folosind cheile: |
k1 = 'Smerenie' | k1 = 'Smerenie' | ||
Line 109: | Line 379: | ||
k2 = 'Dragoste' | k2 = 'Dragoste' | ||
- | Primul plaintext corespunzator lui c1 este m1='Fericiti cei saraci cu duhul, ca'. Gasiti al doilea plaintext m2 ce ii corespunde lui c2. | + | Primul plaintext corespunzator lui c1 este m1='Fericiti cei saraci cu duhul, ca'. Găsiți al doilea plaintext m2 ce îi corespunde lui c2. |
<note> | <note> | ||
- | In biblioteca Pycrypto, DES primeste o cheie pe 8 octeti (64 biti) chiar daca ar trebui sa primeasca numai 7 octeti (56 biti). Ultimul bit din fiecare octet este considerat ca fiind un bit de paritate, dar este ignorat de biblioteca. Astfel cheia ce se foloseste va fi intr-adevar de dimensiunea 56 biti. Pentru simplitate insa, in acest exercitiu vom considera ca dimensiunea cheiei este de 64 biti. Astfel putem folosi cheile de 8 caractere de mai sus pentru DES. | + | În biblioteca Pycrypto, DES primește o cheie pe 8 octeți (64 biți) chiar dacă ar trebui să primeasca numai 7 octeti (56 biti). Ultimul bit din fiecare octet este considerat ca fiind un bit de paritate, dar este ignorat de bibliotecă. Astfel cheia ce se folosește va fi într-adevăr de dimensiunea 56 biți. Pentru simplitate însă, în acest exercițiu vom considera că dimensiunea cheiei este de 64 biți. Astfel putem folosi cheile de 8 caractere de mai sus pentru DES. |
- | Vom considera de asemenea ca initializam DES-ul cu valorile default (i.e. modul ECB si niciun IV). | + | Vom considera de asemenea că inițializăm DES-ul cu valorile default (i.e. modul ECB și niciun IV). |
</note> | </note> | ||
- | Decriptati intregul ciphertext (c1 || c2) cu cheile k1 si k2 folosind 2DES si asigurati-va ca rezultatul dat este mesajul initial m1||m2. | + | Decriptați întregul ciphertext (c1 || c2) cu cheile k1 și k2 folosind 2DES și asigurați-vă că rezultatul dat este mesajul inițial m1||m2. |
- | === D. Implementati atacul meet-in-the-middle pentru 2DES === | + | === D. Implementați atacul meet-in-the-middle pentru 2DES (3p) === |
- | In ultimul si cel mai important pas vi se dau niste perechi plaintext/ciphertext obtinute prin criptarea cu 2DES cu niste chei necunoscute: | + | În ultimul și cel mai important pas vi se dau niște perechi plaintext/ciphertext obținute prin criptarea cu 2DES cu niște chei necunoscute: |
- | m1 = 'Pocainta' (in byte string, i.e. poate fi dat direct ca parametru catre pycrypto DES) | + | m1 = 'Pocainta' (în byte string, i.e. poate fi dat direct ca parametru către pycrypto DES) |
- | c1 = '9f98dbd6fe5f785d' (in hex string, trebuie sa il decodificati mai intai) | + | c1 = '9f98dbd6fe5f785d' (în hex string, trebuie să îl decodificați mai întâi) |
m2 = 'Iertarea' | m2 = 'Iertarea' | ||
Line 131: | Line 401: | ||
c2 = '6e266642ef3069c2' | c2 = '6e266642ef3069c2' | ||
- | Vi se mai dau si ultimii 6 bytes ale celor 2 chei (care sunt altele decat cele folosite la pasii anteriori): | + | Vi se mai dau și ultimii 6 bytes ale celor 2 chei (care sunt altele decat cele folosite la pașii anteriori): |
k1 (last 6 bytes) = 'oIkvH5' | k1 (last 6 bytes) = 'oIkvH5' | ||
Line 137: | Line 407: | ||
k2 (last 6 bytes) = 'GK4EoU' | k2 (last 6 bytes) = 'GK4EoU' | ||
- | Sarcina voastra este sa gasiti restul octetilor din k1 si k2 aplicand atacul meet-in-the-middle. | + | Sarcina voastră este să găsiți restul octeților din k1 și k2 aplicând atacul meet-in-the-middle. |
- | Your task is now to find the full keys k1 and k2 by applying the meet-in-the-middle attack over 2DES. | + | |
- | Pentru a construi un tabel, recomandam sa folositi o lista de tupluri unde adaugati perechi de forma (cheie, encriptie) ca in urmatorul exemplu: | + | Pentru a construi un tabel, recomandăm să folosiți o lista de tupluri unde adăugați perechi de forma (cheie, encripție) ca în următorul exemplu: |
<code> | <code> | ||
tb = [] | tb = [] | ||
Line 151: | Line 420: | ||
</code> | </code> | ||
- | Pentru a realiza cautare binara in tabel, selectati mai intai a doua coloana (pentru a cauta dupa encriptie): | + | Pentru a realiza căutarea binară în tabel, selectați mai întâi a doua coloană (pentru a căuta după encripție): |
<code> | <code> | ||
tenc = [value for _,value in tbs] | tenc = [value for _,value in tbs] | ||
</code> | </code> | ||
- | iar apoi folositi biblioteca bisect (e.g. bisect.bisect_left): | + | iar apoi folosiți biblioteca bisect (e.g. bisect.bisect_left): |
https://docs.python.org/2/library/bisect.html | https://docs.python.org/2/library/bisect.html | ||
Scheletul de cod: | Scheletul de cod: | ||
- | <code python desmitm.py> | + | <spoiler Click pentru a vedea lab05.py> |
- | from utils import * | + | <file python lab05.py> |
- | from operator import itemgetter | + | |
import bisect | import bisect | ||
+ | from operator import itemgetter | ||
+ | from typing import List | ||
+ | |||
from Crypto.Cipher import DES | from Crypto.Cipher import DES | ||
+ | from utils import * | ||
- | def get_index(a, x): | + | |
- | """Locate the leftmost value exactly equal to x in list a""" | + | def get_index(a: List[bytes], x: bytes) -> int: |
+ | """Locate the leftmost value exactly equal to x in list a | ||
+ | |||
+ | Args: | ||
+ | a (List[bytes]): the list in which to search | ||
+ | x (bytes): the value to be searched | ||
+ | |||
+ | Returns: | ||
+ | int: The leftmost index at which the value is found in the list, | ||
+ | or -1 if not found | ||
+ | """ | ||
i = bisect.bisect_left(a, x) | i = bisect.bisect_left(a, x) | ||
if i != len(a) and a[i] == x: | if i != len(a) and a[i] == x: | ||
Line 174: | Line 456: | ||
return -1 | return -1 | ||
- | def des_enc(k, m): | + | |
+ | def des_enc(k: bytes, m: bytes) -> bytes: | ||
""" | """ | ||
Encrypt a message m with a key k using DES as follows: | Encrypt a message m with a key k using DES as follows: | ||
c = DES(k, m) | c = DES(k, m) | ||
- | |||
- | Args: | ||
- | m should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') | ||
- | k should be a bytestring of length exactly 8 bytes. | ||
Note that for DES the key is given as 8 bytes, where the last bit of | Note that for DES the key is given as 8 bytes, where the last bit of | ||
- | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. | + | each byte is just a parity bit, giving the actual key of 56 bits, as |
- | The parity bits are ignored. | + | expected for DES. The parity bits are ignored. |
+ | |||
+ | Args: | ||
+ | k (str): bytestring of length exactly 8 bytes. | ||
+ | m (str): bytestring containing the message (i.e. a sequence of | ||
+ | characters such as 'Hello' or '\x02\x04') | ||
Return: | Return: | ||
- | The bytestring ciphertext c | + | bytes: The bytestring ciphertext c |
""" | """ | ||
d = DES.new(k, DES.MODE_ECB) | d = DES.new(k, DES.MODE_ECB) | ||
Line 194: | Line 478: | ||
return c | return c | ||
- | def des_dec(k, c): | + | |
+ | def des_dec(k: bytes, c: bytes) -> bytes: | ||
""" | """ | ||
Decrypt a message c with a key k using DES as follows: | Decrypt a message c with a key k using DES as follows: | ||
m = DES(k, c) | m = DES(k, c) | ||
- | |||
- | Args: | ||
- | c should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') | ||
- | k should be a bytestring of length exactly 8 bytes. | ||
Note that for DES the key is given as 8 bytes, where the last bit of | Note that for DES the key is given as 8 bytes, where the last bit of | ||
- | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. | + | each byte is just a parity bit, giving the actual key of 56 bits, as |
- | The parity bits are ignored. | + | expected for DES. The parity bits are ignored. |
+ | |||
+ | Args: | ||
+ | k (str): bytestring of length exactly 8 bytes. | ||
+ | c (str): bytestring containing the ciphertext (i.e. a sequence of | ||
+ | characters such as 'Hello' or '\x02\x04') | ||
Return: | Return: | ||
- | The bytestring plaintext m | + | bytes: The bytestring plaintext m |
""" | """ | ||
d = DES.new(k, DES.MODE_ECB) | d = DES.new(k, DES.MODE_ECB) | ||
m = d.decrypt(c) | m = d.decrypt(c) | ||
return m | return m | ||
- | | ||
- | def main(): | ||
- | # Exercitiu pentru test des2_enc | ||
- | key1 = 'Smerenie' | ||
- | key2 = 'Dragoste' | ||
- | m1_given = 'Fericiti cei saraci cu duhul, ca' | ||
- | c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | ||
- | # TODO: implement des2_enc and des2_dec | ||
- | m1 = des2_dec(key1, key2, hex_2_str(c1)) | ||
- | print('ciphertext: ' + c1) | ||
- | print('plaintext: ' + m1) | ||
- | print('plaintext in hexa: ' + str_2_hex(m1)) | ||
- | | ||
- | # TODO: run meet-in-the-middle attack for the following plaintext/ciphertext | ||
- | m1 = 'Pocainta' | ||
- | c1 = '9f98dbd6fe5f785d' # in hex string | ||
- | m2 = 'Iertarea' | ||
- | c2 = '6e266642ef3069c2' | ||
- | | ||
- | # Note: you only need to search for the first 2 bytes of the each key: | ||
- | k1 = '??oIkvH5' | ||
- | k2 = '??GK4EoU' | ||
- | if __name__ == "__main__": | + | def des2_enc(k1: bytes, k2: bytes, m: bytes) -> bytes: |
- | main() | + | # TODO B.1: implement des2_enc |
- | </code> | + | raise NotImplementedError("Not implemented") |
+ | |||
+ | def des2_dec(k1: bytes, k2: bytes, c: bytes) -> bytes: | ||
+ | # TODO B.2: implement des2_dec | ||
+ | raise NotImplementedError("Not implemented") | ||
+ | |||
+ | |||
+ | def test_des2() -> None: | ||
+ | k1 = "Smerenie" | ||
+ | k2 = "Dragoste" | ||
+ | m1_given = "Fericiti cei saraci cu duhul, ca" | ||
+ | c1 = "cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32" | ||
+ | c2 = "54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf" | ||
+ | |||
+ | # TODO C: Decrypt c1 and c2 using k1 and k2, and make sure that | ||
+ | # des2_dec(k1, k2, c1 || c2) == m1 || m2 | ||
+ | |||
+ | # TODO C.1: Convert k1, k2, c1, and c2 to bytes. It may make the exercise | ||
+ | # easier to implement. Use string_to_bytes() for plain texts (i.e., string | ||
+ | # in human-readable format), and bytes.fromhex() for hex strings. | ||
+ | k1 = ... | ||
+ | k2 = ... | ||
+ | c1 = ... | ||
+ | c2 = ... | ||
+ | |||
+ | # NOTE: The code to decrypt c1 is already provided below. You **need** | ||
+ | # to decrypt c2 as well. | ||
+ | m1 = bytes_to_string(des2_dec(k1, k2, c1)) | ||
+ | assert m1 == m1_given, f'Expected "{m1_given}", but got "{m1}"' | ||
+ | |||
+ | print("ciphertext:", c1) | ||
+ | print("plaintext:", m1) | ||
+ | print("plaintext in hexa:", str_2_hex(m1)) | ||
+ | |||
+ | # TODO C.2: Decrypt m2 similar to m1. Keep in mind that des_dec() | ||
+ | # returns bytes | ||
+ | m2 = ... | ||
+ | |||
+ | print("ciphertext:", c2) | ||
+ | print("plaintext:", m2) | ||
+ | print("plaintext in hexa:", str_2_hex(m2)) | ||
+ | |||
+ | # TODO C.3: Just to make sure you implemented the task correctly: | ||
+ | # des2_dec(k1, k2, c1 || c2) == m1 || m2 | ||
+ | m12 = ... | ||
+ | assert m12 == m1 + m2, f'Expected "{m12}" to equal "{m1 + m2}"' | ||
+ | |||
+ | |||
+ | def mitm() -> None: | ||
+ | # TODO D: run meet-in-the-middle attack for the following plaintext/ciphertext | ||
+ | m1 = "Pocainta" | ||
+ | c1 = "9f98dbd6fe5f785d" | ||
+ | m2 = "Iertarea" | ||
+ | c2 = "6e266642ef3069c2" | ||
+ | |||
+ | # TODO D.1: Convert m1, m2, c1, and c2 to bytes. It may make the exercise | ||
+ | # easier to implement. Use string_to_bytes() for plain texts (i.e., string | ||
+ | # in human-readable format), and bytes.fromhex() for hex strings. | ||
+ | m1 = ... | ||
+ | c1 = ... | ||
+ | m2 = ... | ||
+ | c2 = ... | ||
+ | |||
+ | # NOTE: You only need to search for the first 2 bytes of the each key (i.e., | ||
+ | # to find out what are the values for each `?`). | ||
+ | k1 = "??oIkvH5" | ||
+ | k2 = "??GK4EoU" | ||
+ | |||
+ | # TODO D.2: Generate the table containing (k2, DES(k2, m1)), for every | ||
+ | # possible k2. Use a List[Tuple[bytes, bytes]] for the table (see task | ||
+ | # description above). | ||
+ | |||
+ | # TODO D.3: Sort the table based on the ciphertexts. Extract the ciphertexts | ||
+ | # in a list (see task description above). | ||
+ | |||
+ | # TODO D.4: Perform binary search for all possible k1, such that . | ||
+ | # Save the set of candidate keys (k1, k2) in a list. | ||
+ | |||
+ | # TODO D.5: From the set of candidate keys, print the ones matching | ||
+ | # the second constraint: 2DES(k1, k2, m2) == c2 | ||
+ | |||
+ | |||
+ | def main() -> None: | ||
+ | test_des2() | ||
+ | mitm() | ||
+ | |||
+ | |||
+ | if __name__ == "__main__": | ||
+ | main()</file> | ||
+ | </spoiler> | ||
+ | </hidden> |