This is an old revision of the document!


Laboratorul 03 - PRGs

Prezentarea PowerPoint pentru acest laborator poate fi găsită aici.

utils.py
import base64
 
# CONVERSION FUNCTIONS
 
def _chunks(string, chunk_size):
    for i in range(0, len(string), chunk_size):
        yield string[i:i+chunk_size]
 
def _hex(x):
    return format(x, '02x')
 
def hex_2_bin(data):
    return ''.join(f'{int(x, 16):08b}' for x in _chunks(data, 2))
 
def str_2_bin(data):
    return ''.join(f'{ord(c):08b}' for c in data)
 
def bin_2_hex(data):
    return ''.join(f'{int(b, 2):02x}' for b in _chunks(data, 8))
 
def str_2_hex(data):
    return ''.join(f'{ord(c):02x}' for c in data)
 
def bin_2_str(data):
    return ''.join(chr(int(b, 2)) for b in _chunks(data, 8))
 
def hex_2_str(data):
    return ''.join(chr(int(x, 16)) for x in _chunks(data, 2))
 
# XOR FUNCTIONS
 
def strxor(a, b):  # xor two strings, trims the longer input
    return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b))
 
def bitxor(a, b):  # xor two bit-strings, trims the longer input
    return ''.join(str(int(x) ^ int(y)) for (x, y) in zip(a, b))
 
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)))
 
# BASE64 FUNCTIONS
 
def b64decode(data):
    return bytes_to_string(base64.b64decode(string_to_bytes(data)))
 
def b64encode(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

Exercițiul 1

In acest exercitiu vom incerca sa spargem un LCG (Linear Congruential Generator), ce se poate folosi pentru a genera numere “slab” aleatoare. Am folosit un astfel de generator pentru a obtine o secventa de octeti cu care a fost criptat un mesaj (plaintext). Textul cifrat (ciphertext) in sistemul hexazecimal este urmatorul:

a432109f58ff6a0f2e6cb280526708baece6680acc1f5fcdb9523129434ae9f6ae9edc2f224b73a8

Dupa cum stiti, LCG foloseste urmatoare formula pentru a genera octeti:

s_next = (a * s_prev + b) mod p

unde s_next si s_prev sunt octeti (0-255) generati, p = 257 iar a si b au valori intre 0 si 256.

Mai stiti si ca primele 16 litere din plaintext sunt “Let all creation”. Criptarea a fost obtinuta executand XOR intre plaintext si secventa de octeti consecutivi generati de LCG

Spargeti LCG astfel incat sa puteti prezice sirul de numere generate “aleator”. Care este mesajul initial?

Puteti folosi urmatorul schelet de cod:

'ex1_weak_rng.py'
from utils import *
 
#Parameters for weak LC RNG
class WeakRNG:
    "Simple class for weak RNG"
    def __init__(self):
        self.rstate = 0
        self.maxn = 255
        self.a = 0 #Set this to correct value
        self.b = 0 #Set this to correct value
        self.p = 257
 
    def init_state(self):
        "Initialise rstate"
        self.rstate = 0 #Set this to some value
        self.update_state()
 
    def update_state(self):
        "Update state"
        self.rstate = (self.a * self.rstate + self.b) % self.p
 
    def get_prg_byte(self):
        "Return a new PRG byte and update PRG state"
        b = self.rstate & 0xFF
        self.update_state()
        return b
 
 
def main():
 
    #Initialise weak rng
    wr = WeakRNG()
    wr.init_state()
 
    #Print ciphertext
    CH = 'a432109f58ff6a0f2e6cb280526708baece6680acc1f5fcdb9523129434ae9f6ae9edc2f224b73a8'
    print("Full ciphertext in hexa: " + CH)
 
    #Print known plaintext
    pknown = 'Let all creation'
    nb = len(pknown)
    print("Known plaintext: " + known)
    pkh = str_2_hex(pknown)
    print("Plaintext in hexa: " + pkh)
 
    #Obtain first nb bytes of RNG
    gh = hexxor(pkh, CH[0:nb*2])
    print(gh)
    gbytes = []
    for i in range(nb):
        gbytes.append(ord(hex_2_str(gh[2*i:2*i+2])))
    print("Bytes of RNG: ")
    print(gbytes)
 
    #Break the LCG here:
    #1. find a and b
    #2. predict/generate rest of RNG bytes
    #3. decrypt plaintext
 
    # Print full plaintext
    p = ''
    print("Full plaintext is: " + p)
 
 
if __name__ == "__main__":
    main()  

Exercițiul 2

Avantaje. Scopul acestui exercitiu e de a clarifica conceptul de avantaje prezentat la curs. Vom considera doua expermimente $\mathsf{EXP}(0)$ si $\mathsf{EXP}(1)$:

  • In $\mathsf{EXP}(0)$ verificatorul da cu banul (probabilitate de $1/2$ pentru CAP si $1/2$ pentru PAJURA) si transmite rezultatul adversarului $\mathsf{A}$.
  • In $\mathsf{EXP}(1)$ verificatorul intotdeauna va transmite PAJURA catre adversar.

Consideram $r = 0$ pentru CAP si $r = 1$ pentru PAJURA. Jocul va arata astfel:

Scopul adversarului este este sa diferentieze intre cele doua experimente: la finalul fiecarui experiment, adversarul returneaza un bit $\mathsf{b'}$, $0$ sau $1$, in speranta de a ghici $\mathsf{EXP}(b) = \mathsf{EXP}(b')$. Fie $W_{b}$ evenimentul cand in experimentul $\mathsf{EXP}(b)$ adversarul returneaza $b' = 1$. Adversarul vrea sa isi maximizeze avantajul de a distinge experimentele. Mai exact, el vrea sa maximizeze valoarea $\mathsf{Adv} = \left| \mathsf{Pr}\left[W_{0}\right] − \mathsf{Pr}\left[W_{1}\right] \right| \in \left[0, 1\right]$.

Avantajul $\mathsf{Adv}$ reda capacitatea adversarului de a diferentia experimentele. Daca avantajul este $0$, atunci advesarul se comporta identic in ambele cazuri si nu poate sa le distinga. Daca avantajul este $1$, atunci adversarul stie concret care este valoarea lui $b$. Daca avantajul este neglijabil pentru orice adversar eficient atunci putem spune ca nu se poate distinge intre experimente.

a. Calculati avantajul pentru fiecare dintre urmatorii adversari:

  • A1: Returneaza mereu $b'=1$.
  • A2: Ignora valoarea primita de la verificator si returneaza aleator $0$ sau $1$ cu aceeasi probabilitate.
  • A3: Returneaza $1$ daca a primit CAP ($r=0$) de la verificator, altfel returneaza $0$.
  • A4: Returneaza $0$ daca a primit CAP de la verificator, altfel returneaza $1$.
  • A5: Daca a primit CAP returneaza $1$. Daca a primit PAJURA returneaza aleator $0$ sau $1$ cu aceeasi probabilitate.
  • A6: Daca a primit CAP returneaza aleator $0$ sau $1$ cu aceeasi probabilitate. Daca a primit PAJURA returneaza $0$.

b. Care este avantajul maxim pe care il poate obtine un adversar? De ce?

Incercati sa obtineti mai intai o formula generala ce nu depinde de adversar.

Exercise 3

One of the applications of computing the advantage for an adversary on a cryptographic scheme is to prove that a $\mathsf{PRG}$ is secure or not. Recall from the lecture that $G : K \rightarrow \{0, 1\}^n$ is a secure $\mathsf{PRG}$ if for any efficient (that runs in polynomial time) statistical test $\mathsf{A}$, the advantage $\mathsf{Adv_{PRG}[A, G]}$ is negligible. A statistical test is basically an algorithm that tries to determine whether the input seems random or not, therefore it can be defined as follows:

\begin{equation*} A(x) = \begin{cases} 1 & \text{if x is random}\\ 0 & \text{otherwise} \end{cases} \end{equation*}

You can associate the statistical test $\mathsf{A}$ with an adversary that tries to break the $\mathsf{PRG}$ (i.e. it can distinguish the output of the $\mathsf{PRG}$ from a truly random generator). In this case, we shall define the experiments as follows:

  • In $\mathsf{EXP}(0)$ the challenger sends the output $G(k)$ generated by the $\mathsf{PRG}$ to the adversary $\mathsf{A}$;
  • In $\mathsf{EXP}(1)$ the challenger sends the output $r$ generated by a truly random generator to the adversary.

Also, $W_{b}$ represents the event in which the adversary guesses that the input is random (i.e. $A(x) = 1$), in the experiment $b$. The idea behind this is that if the adversary can tell the difference between the $\mathsf{PRG}$ and a truly random generator, then the $\mathsf{PRG}$ is not secure. Thus, the advantage becomes: $\mathsf{Adv_{PRG}[A, G]} = \left|\underset{k \leftarrow K}{Pr}[A(G(k)) = 1] - \underset{r \leftarrow \{0, 1\}^n}{Pr}[A(r) = 1] \right|$.

Although the problem of determining whether there are provably secure $\mathsf{PRG}s$ is equivalent to solving the well-known problem $\mathsf{P}$ vs $\mathsf{NP}$, in practice there are some $\mathsf{PRG}s$ that are considered secure, by using some heuristics.

In this exercise, you are given a $\mathsf{PRG}$ $G : \{0, 1\}^s \rightarrow \{0, 1\}^n$ that is known to be secure and your assignment is to determine which of the following $\mathsf{PRG}s$ are also secure:

  • $G_{1}(k) = G(k) \oplus 1^n$
  • $G_{2}(k) = G(k) \| 0$
  • $G_{3}(k) = G(0)$
  • $G_{4}(k=(k_1, k_2)) = G(k_1) \| G(k_2)$
  • $G_{5}(k)=G(k) \| G(k)$

For strings $y$ and $z$ we use $y \| z$ to denote the concatenation of $y$ and $z$.

Some of these exercises may not require computing any advantage.

Exercițiul 4

Vom folosi experimentul definit mai devreme pentru a construi un pseudorandom generator ($\mathsf{PRG}$):

  1. Se alege lungimea $n$ a sirului ce va fi generat
  2. Se obtine un sir de biti $R$ de lungime $n$ (e.g. puteti folosi chiar si LCG-ul din Exercitiul 1)
  3. Pentru fiecare bit $r$ din sirul $R$ generat anterior, $\mathsf{PRG}$-ul va intoarce un bit $b$ astfel:
  • daca bitul $r$ este $0$, atunci intoarce un bit aleator $b \in \{0, 1\}$
  • daca bitul $r$ este $1$, atunci intoarce $1$

a. Implementati frequency (monobit) test asa cum e descris in articolul din NIST (vezi section 2.1). Verificati daca secventa generata de $\mathsf{PRG}$-ul de mai sus este aleatoare sau nu. (presupunem $n=100$)

b. Executati testul pe un sir aleator de biti (puteti folosi sirul R folosit in $\mathsf{PRG}$) si comparati rezultatele.

Daca rezultatele celor 2 experimente difera in mod regulat, acest test ofera un atacator care sparge $\mathsf{PRG}$-ul descris mai sus.

Pentru a genera un sir aleator de biti, puteti folosi o functie de forma:

import random
 
def get_random_string(n): #generate random bit string
    bstr = bin(random.getrandbits(n)).lstrip('0b').zfill(n)
    return bstr

In Python puteti folosi functii precum sqrt, fabs sau erfc din modulul math

import math

Exercițiul 5 (Bonus)

In acest exercitiu, veti incerca sa verificati daca intr-adevar Salsa20 stream cipher este mai rapid decat RC4. Descarcati aceasta arhiva. Rulati programul folosind acelasi fisier de intrare atat pentru Salsa20 cat si pentru RC4. Comparati timpul de executie in ambele cazuri.

Pentru sistemele UNIX (si WSL), folositi scriptul prepare.sh pentru a complila sursele si a genera fisierele necesare. Daca nu folositi WSL pe Windows, scriptul prepare.ps1 genereaza fisierele necesare dar nu si compileaza sursele.

Folositi optiunea ”-h” pentru a afisa lista tuturor argumentelor folosite de program.

Uitati-va in fisierul salsa20.h. Unde se executa rundele de criptare?

Incercati sa implementati aceeasi functionalitate folosint OpenSSL. OpenSSL suporta RC4 dar nu si Salsa20. Folositi ChaCha20 in loc de Salsa20, aceasta fiind o varianta imbunatatita a algoritmului.

ic/labs/03.1634219073.txt.gz · Last modified: 2021/10/14 16:44 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