This shows you the differences between two versions of the page.
ic:labs:08 [2021/01/25 11:13] razvan.smadu |
ic:labs:08 [2023/11/22 01:03] (current) razvan.smadu [Exercițiul 1 - OpenSSL în linia de comanda (3p)] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 08 - MACs ===== | + | ===== Laboratorul 08 - MAC ===== |
- | The powerpoint presentation for this lab can be found [[https://drive.google.com/file/d/188HfjYdDqc97bPXiaF6tccVcU4pSy6-f/view?usp=sharing|here]]. | + | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/188HfjYdDqc97bPXiaF6tccVcU4pSy6-f/view?usp=sharing|aici]]. |
- | In this lab we'll do some exercises with Message Authentication Codes. | + | ==== Exercițiul 1 - OpenSSL în linia de comanda (3p) ==== |
- | ==== Exercise 1 - Existential Unforgeability ==== | + | OpenSSL este o suita de funcții criptografice, care oferă atât o facilitate de unelte în linie de comandă |
+ | cât și o librărie folosită pentru aplicații (e.g. C/Python). Aici vom face câteva exerciții cu OpenSSL | ||
+ | în linie de comandă ca să pridenți ceva experiență cu aceste funcții foarte populare și utile. | ||
- | In this exercise we will attack an insecure MAC algorithm by showing that an adversary can forge a (message, tag) pair without first querying a $\mathsf{Tag}$ oracle with the message. | + | Acum: ati primit niste fisiere top secret in arhiva {{:ic:labs:top_secret.zip|top secret}}. |
+ | Aceasta contine un fisier binar (data_enc_mac.bin) cu un mesaj criptat + un MAC, i.e. | ||
- | Let $F$ be a $\mathsf{PRF}$. Show that the following MAC is insecure by constructing an efficient adversary with non-negligible advantage. The key is $k \in \{0, 1\}^n$, and for any message $m = m1 \| m2$ with $\left|m_1\right| = \left|m_2\right| = n$, the MAC is computed as: | + | file = enc(ke,m) + mac-cbc(km,m) |
+ | |||
+ | Vreti sa aflati mesajul secret si sa verificati ca este corect (mac corect). | ||
+ | Pentru asta va trebuie cheile de criptare (ke) si de MAC (km). | ||
+ | Stim ca criptarea s-a facut cu aes-128-cbc cu iv=000.00 (16 octeti) iar mac-ul este CMAC cu AES-128-CBC. | ||
+ | |||
+ | Din fericire mai avem in arhiva un fisier (tot binar, data_kek.bin.pass_hellokitty) care contine un master key (k0), dar care | ||
+ | este criptat si el cu aes-128-cbc si pbkdf2, cu passphrase din numele fisierului. | ||
+ | |||
+ | Mai stim ca putem folosi acest master key (k0) pentru a deriva cheile de criptare si MAC de mai sus (ke, km) | ||
+ | prin aplicarea functiei SHA256, i.e. (ke, km) = SHA256(k0) | ||
+ | |||
+ | Acum stim cam tot ce avem nevoie pentru a afla si a verifica mesajul secret. | ||
+ | Care este acest mesaj? | ||
+ | |||
+ | Vedeti [[https://www.openssl.org/docs/man1.1.1/man1/enc.html|aici]] informatii ajutatoare pentru criptare si criptare cu pbkdf, [[https://www.openssl.org/docs/man1.1.1/man1/openssl-dgst.html|aici]] informatii pentru SHA256 si | ||
+ | [[https://www.openssl.org/docs/man3.0/man1/openssl-mac.html|aici]] pentru MAC. | ||
+ | |||
+ | * Incepeti prin verificarea versiunii de OpenSSL (eu am folosit 3.0.2): | ||
+ | <code> | ||
+ | #openssl version | ||
+ | </code> | ||
+ | |||
+ | * Apoi decriptati fisierul binar kek cu passphrase-ul din numele fisierului pentru a obtine k0 (vedeti openssl enc ... -pbkdf2...). | ||
+ | <code> | ||
+ | #openssl enc -d -aes128 -pbkdf2 -in... | ||
+ | </code> | ||
+ | |||
+ | * Acum folositi SHA256 pentru a obtine (ke|km) = SHA256(k0). Vedeti openssl -dgst. Puteti face pipe ca sa trimiteti date de intrare catre SHA256 sau alte functii openssl, e.g.: | ||
+ | <code> | ||
+ | #echo -n $k0 | openssl dgst -sha256 | ||
+ | </code> | ||
+ | unde k0 contine cheia aflata la punctul precedent (hint: ar trebui sa fie gustoasa...). | ||
+ | |||
+ | |||
+ | * Cu ke (primii 16 octeti din rezultatul de mai sus) putem decripta mesajul (folositi -nopad, nu e nevoie de padding). Vedeti openssl enc -d .... Aici trebuie sa folositi primii N-16 octeti din fisier (ultimii 16 sunt MAC-ul). Puteti trunchia cu cat sau alta metoda la alegere, e.g.: | ||
+ | <code> | ||
+ | #cat data_enc_mac.bin | head -c80 | ||
+ | </code> | ||
+ | |||
+ | apoi folositi iarasi openssl enc -aes128 -d -K... -iv... -nopad (cu parametru in fisier sau cu pipe ca mai sus) | ||
+ | ca sa decriptati mesajul. | ||
+ | |||
+ | * In sfarsit, puteti verifica ca mesajul primit este corect (desi ar trebui sa para un text inteligibil) prin aplicarea MAC-ului CMAC cu AES-128-CBC peste ultimii 16 octeti din fisierul binar data_enc_mac.bin. Vedeti functia | ||
+ | <code> | ||
+ | openssl mac -cipher AES-128-CBC -binary | ||
+ | </code> | ||
+ | cu parametrul -macopt hexkey:... (aici km in hexa) | ||
+ | |||
+ | |||
+ | ==== Exercițiul 2+ ==== | ||
+ | |||
+ | Pentru al doilea exercitiu puteti folosi platforma Google Colab, accesând [[https://colab.research.google.com/github/ACS-IC-labs/IC-labs/blob/main/labs/lab08/lab8.ipynb|acest]] link. | ||
+ | |||
+ | <hidden> | ||
+ | |||
+ | |||
+ | În acest laborator vom studia despre Message Authentication Codes (MAC). | ||
+ | |||
+ | ==== Exercițiul 1 - Existential Unforgeability ==== | ||
+ | |||
+ | În acest exercițiu vom ataca un algoritm nesigur din categoria MAC prin a arăta că un adversar poate falsifica o pereche de tipul (mesaj, tag), fără a avea acces la un oracol $\mathsf{Tag}$ care returnează tag pentru mesajul dorit. | ||
+ | |||
+ | Fie $F$ un $\mathsf{PRF}$. Arătați că următorul MAC este nesigur prin a construi un adversar eficient ce prezintă un avantaj ne-neglijabil împotriva algoritmului MAC. Cheia este $k \in \{0, 1\}^n$, iar pentru fiecare mesaj $m = m1 \| m2$ cu $\left|m_1\right| = \left|m_2\right| = n$, MAC-ul este calculat folosind următoarea formulă: | ||
$\mathsf{Tag}(k, m_1 \| m_2) = F_k(m_1) \| F_k(F_k(m_2)) $ | $\mathsf{Tag}(k, m_1 \| m_2) = F_k(m_1) \| F_k(F_k(m_2)) $ | ||
<note tip> | <note tip> | ||
- | You may try to use two queries to break the security of this MAC. | + | Ați putea încerca să spargeți securitatea algoritmului MAC folosind 2 query-uri. Cu toate acestea, puteți găsi o metodă să atacați algoritmul folosind doar un query? |
- | But can you do it with a single query ? | + | |
</note> | </note> | ||
+ | </hidden> | ||
- | <hidden>==== Exercise 2 - Timing attack ==== | + | <hidden>==== Exercițiul 2 - Timing attack ==== |
- | + | ||
- | In this exercise you will perform a timing attack against CBC-MAC. | + | |
- | You are given access to a CBC-MAC $\mathsf{Verify}$ oracle, which tests whether the received tag matches the one computed using the secret key. Timing attacks exploit naive equality comparisons between the received and computed MACs (for example, the comparison is done byte by byte; more checks means more latency). | + | În acest exercițiu vom implementa Timing attack pentru algoritmul CBC-MAC. |
- | Your task is to produce a forged tag for the message 'Hristos a inviat' without knowing the key. Do this by iterating through all possible values for a specific byte; when the oracle's latency for a certain value seems larger than the rest, it suggests that the equality test returned True and the oracle passed to the next byte. | + | Pentru realizarea atacului aveți acces la un oracol $\mathsf{Verify}$ de verificare pentru algoritmul CBC-MAC, care testează dacă tag-ul primit ca input este același cu cel calculat de oracol folosind cheia secretă. Timing attack este folosit pentru a ataca implementări nesigure în care se verifică egalitatea dintre un MAC primit ca input și cel calculat intern de algoritm (ex. verificarea este făcută byte cu byte, ceea ce conduce la o latență mai mare pentru mai mulți bytes verificați). |
- | Try to start by finding the first byte and checking your result with the timing attack. | + | Task-ul vostru este să produceți un tag falsificat pentru mesajul 'Hristos a inviat' fără a cunoaște cheia secretă. Realizați acest lucru iterând prin toate posibilele valori ale unui byte; când latența oracolului pentru o anumită valoare pare mai mare decât pentru celelalte, înseamnă că verificarea de egalitate a returnat True și că oracolul a trecut la verificarea următorului byte. |
- | You can use ''time.clock()'' before and after each oracle query to measure its runtime. | + | Pentru măsurarea timpului de execuție pentru un query, puteți folosi ''time.clock()'' înainte și după fiecare query. |
<note important> | <note important> | ||
- | The oracle's latency is subject to noise; for the best results, you may need to run each query multiple times (try 30) and compute the mean latency. | + | Latența oracolului poate fi afectată de zgomot, iar pentru rezultate cât mai bune ale atacului, este recomandat ca fiecare query (30 ori) să fie rulat de mai multe ori, iar latența finală să fie calculată ca media rezultatelor acestor rulări. |
</note> | </note> | ||
- | TODO1: Implement the CBC-MAC function. | + | TODO1: Implementați funcția CBC-MAC. |
<note> | <note> | ||
- | To check your implementation of CBC-MAC is correct, verify that this (plaintext,tag) verifies fine: | + | Pentru a verifica implementarea funcției CBC-MAC, testați că următoarea pereche (plaintext, tag) verifică pentru implementarea voastră: |
plaintext = 'Placinta de mere' | plaintext = 'Placinta de mere' | ||
- | tag = '07d2771038d62b94fce106cff957da0f' (in hex, you need to apply decode to get a bytestream) | + | tag = '07d2771038d62b94fce106cff957da0f' (în hex, trebuie aplicat decode pentru a obține un bytestream) |
</note> | </note> | ||
- | TODO2: Implement the MAC time verification attack and obtain the desired MAC without knowing the key. | + | TODO2: Implementați Timing attack pentru MAC și obțineți MAC-ul dorit fără a cunoaște cheia secretă. |
<note> | <note> | ||
- | Try to print the resulting tag in hex (with tag.encode(hex)). It should start with 51 and end with 81. | + | Încercați să afișați tag-ul rezultat în hex (folosind tag.encode(hex)). Ar trebui ca rezultatul în hex să înceapă cu 51 și să se termine cu 81. |
</note> | </note> | ||
Line 211: | Line 275: | ||
</file> | </file> | ||
</hidden> | </hidden> | ||
+ | <hidden> | ||
- | ==== Exercise 2 - Birthday attack ==== | + | ==== Exercițiul 2 - Birthday attack ==== |
- | In this exercise you will implement the Birthday attack on SHA-1 using OpenSSL. | + | În acest exercițiu vom implementa Birthday attack pentru algoritmul SHA-1 folosind OpenSSL. |
- | The goal is to obtain a collision in the first four bytes of the hash. | + | |
- | Your goal is to obtain a collision by finding two messages, $M_1$ and $M_2$, such that for the first **four** bytes $\mathsf{SHA1}(M_1) = \mathsf{SHA1}(M_2)$. | + | Scopul acestui atac este să obțină o coliziune pentru algoritmul de hashing SHA-1 folosind 2 mesaje $M_1$ și $M_2$, astfel încât pentru **primii 4** bytes cele două hash digest-uri să fie egale ($\mathsf{SHA1}(M_1) = \mathsf{SHA1}(M_2)$). |
- | The collision will be $32$ bits long, which means you will need $2^{16}$ random messages in your attack. Note that the attack is not guaranteed to succeed; on average, two iterations of the attack are required to find a collision. | + | Coliziunea trebuie să aibă o lungime de $32$ biți, ceea ce înseamnă că sunt necesare $2^{16}$ mesaje random pentru a efectua atacul. De reținut este că atacul nu este garantat să funcționeze din prima iterație; în medie, sunt necesare 2 iterații ale atacului pentru a găsi o coliziune. |
- | In contrast to previous labs, this time we'll use C. You can implement the attack from scratch, or start from our {{:sasc:laboratoare:birthday.tar.gz|archive here}}. | + | Față de laboratoarele anterioare, acest laborator va fi realizat în C folosind următorul schelet de cod {{:sasc:laboratoare:birthday.tar.gz|arhivă cod}} sau implementând atacul de la zero. |
- | To compute a digest, you might find the code below useful: | + | Pentru a obține digest-ului algoritmul de hashing SHA1 folosind OpenSSL, puteți folosi următorul exemplu: |
<code C> | <code C> | ||
Line 232: | Line 296: | ||
SHA1_Final(md, &context); /* md must point to at least 20 bytes of valid memory */ | SHA1_Final(md, &context); /* md must point to at least 20 bytes of valid memory */ | ||
</code> | </code> | ||
- | <note tip>You can also check the SHA man page: https://www.openssl.org/docs/manmaster/man3/SHA1.html</note> | + | <note tip>Puteți consulta SHA man page aici: https://www.openssl.org/docs/manmaster/man3/SHA1.html</note> |
<note important> | <note important> | ||
- | You may compile and install OpenSSL from sources. | + | Puteți compila și instala OpenSSL folosind codul sursă. |
- | Download the library from [[https://www.openssl.org/source/openssl-1.1.1d.tar.gz]], and unpack it. | + | Descărcați biblioteca de la următorul link [[https://www.openssl.org/source/openssl-1.1.1d.tar.gz]] și dezarhivați fișierul descărcat. |
- | Open the unpacked folder from bash, and run the following commands: | + | Deschideți folderul dezarhivat și rulați următoarele comenzi folosind bash: |
<code bash> | <code bash> | ||
$ ./config --prefix=your_working_dir --openssldir=your_working_dir/openssl | $ ./config --prefix=your_working_dir --openssldir=your_working_dir/openssl | ||
Line 246: | Line 310: | ||
</code> | </code> | ||
- | To fix the makefile using the new paths, change the variables at the start with the ones below: | + | Pentru a fixa Makefile-ul folosind noile path-uri, modificați variabilele de la începutul Makefile-ului cu următoarele: |
<code makefile> | <code makefile> | ||
LDFLAGS=-Lyour_working_dir/lib -lcrypto | LDFLAGS=-Lyour_working_dir/lib -lcrypto | ||
Line 253: | Line 317: | ||
</note> | </note> | ||
+ | </hidden> | ||
<hidden> | <hidden> | ||
<file python birthday.py> | <file python birthday.py> | ||
- | |||
- | import sys | ||
- | import string | ||
- | import base64 | ||
from Crypto.Hash import SHA256 | from Crypto.Hash import SHA256 | ||
+ | from random import randint | ||
+ | from collections import Counter | ||
def raw2hex(raw): | def raw2hex(raw): | ||
- | return raw.encode('hex') | + | return raw.hex() |
def hex2raw(hexstring): | def hex2raw(hexstring): | ||
- | return base64.b16decode(hexstring) | + | return bytes.fromhex(hexstring) |
hexdigits = '0123456789ABCDEF' | hexdigits = '0123456789ABCDEF' | ||
Line 273: | Line 335: | ||
def hash(message): | def hash(message): | ||
h = SHA256.new() | h = SHA256.new() | ||
- | h.update(message) | + | h.update(message.encode()) |
return h.digest() | return h.digest() | ||
- | |||
def main(): | def main(): | ||
# Try to find a collision on the first 4 bytes (32 bits) | # Try to find a collision on the first 4 bytes (32 bits) | ||
- | | + | |
# Step 1. Generate 2^16 different random messages | # Step 1. Generate 2^16 different random messages | ||
- | | + | |
# Step 2. Compute hashes | # Step 2. Compute hashes | ||
- | | + | |
# Step 3. Check if there exist two hashes that match in the first | # Step 3. Check if there exist two hashes that match in the first | ||
# four bytes. | # four bytes. | ||
- | | + | |
# Step 3a. If a match is found, print the messages and hashes | # Step 3a. If a match is found, print the messages and hashes | ||
- | | + | |
# Step 3b. If no match is found, repeat the attack with a new set | # Step 3b. If no match is found, repeat the attack with a new set | ||
# of random messages | # of random messages |