This shows you the differences between two versions of the page.
|
ic:labs:10 [2021/10/01 23:18] tiberiu.iorgulescu |
ic:labs:10 [2023/10/09 23:23] (current) razvan.smadu |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ===== Laboratorul 10 - Prezentul și Viitorul criptării cu chei publice (Public Key Encryption) ===== | ===== Laboratorul 10 - Prezentul și Viitorul criptării cu chei publice (Public Key Encryption) ===== | ||
| - | Prezentarea PowerPoint pentru acest laborator o puteți găsi [[https://drive.google.com/file/d/1wi11KjB6wiCpz7lcrhNUrRzIAXcHokd1/view?usp=sharing|aici]]. | + | Prezentarea PowerPoint pentru acest laborator o puteți găsi [[https://drive.google.com/file/d/1wi11KjB6wiCpz7lcrhNUrRzIAXcHokd1/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/lab10/lab10.ipynb|acest]] link. |
| + | |||
| + | <hidden> | ||
| În acest laborator vom face niște exerciții strașnice folosind metode de criptare cu chei publice pentru schimb de chei (key exchange) și criptare de date. | În acest laborator vom face niște exerciții strașnice folosind metode de criptare cu chei publice pentru schimb de chei (key exchange) și criptare de date. | ||
| Line 12: | Line 14: | ||
| Clientul și serverul au o structură similară. Fiecare are trebui să construiască o cheie publică, pe care să o trimită celeilalte părți, iar în final să își calculeze cheia secretă. Scopul vostru este să completați părțile lipsă din cod. | Clientul și serverul au o structură similară. Fiecare are trebui să construiască o cheie publică, pe care să o trimită celeilalte părți, iar în final să își calculeze cheia secretă. Scopul vostru este să completați părțile lipsă din cod. | ||
| - | Pentru aceasta, consultați documentația openssl de [[https://www.openssl.org/docs/man1.1.0/crypto/|aici]]. Din moment ce sunt similare, concentrați-vă numai pe unul dintre ele și completați în mod asemănător și în cealaltă parte. | + | Pentru aceasta, consultați documentația openssl de [[https://www.openssl.org/docs/man1.1.1/man3/|aici]]. Din moment ce sunt similare, concentrați-vă numai pe unul dintre ele și completați în mod asemănător și în cealaltă parte. |
| Fișierul Makefile ar trebui să vă ajute să faceți build la ambele. Folosiți comanda 'make all'. | Fișierul Makefile ar trebui să vă ajute să faceți build la ambele. Folosiți comanda 'make all'. | ||
| Line 19: | Line 21: | ||
| Dacă totul merge bine, ar trebui să vedeți același secret atât la client, cât și la server. | Dacă totul merge bine, ar trebui să vedeți același secret atât la client, cât și la server. | ||
| - | Înainte să începeți acest task, verificați dacă aveți openSSL instalat (în acest laborator vom folosi openSSL 1.1.0): | + | Înainte să începeți acest task, verificați dacă aveți openSSL instalat (în acest laborator vom folosi openSSL 1.1.1): |
| <code> | <code> | ||
| #openssl version | #openssl version | ||
| Line 27: | Line 29: | ||
| <note> | <note> | ||
| - | Dacă aveți nevoie să instalați openSSL pe calculatorul vostru, descărcați openssl 1.1.0 de [[https://www.openssl.org/source/openssl-1.1.0c.tar.gz|aici]]. | + | Dacă aveți nevoie să instalați openSSL pe calculatorul vostru, descărcați openssl 1.1.1 de [[https://www.openssl.org/source/openssl-1.1.1d.tar.gz|aici]]. |
| Salvați fișierul într-un folder local accesibil, apoi compilați-l și instalați-l într-un folder. | Salvați fișierul într-un folder local accesibil, apoi compilați-l și instalați-l într-un folder. | ||
| Deschideți folder-ul în bash și rulați următoarele comenzi: | Deschideți folder-ul în bash și rulați următoarele comenzi: | ||
| Line 43: | Line 45: | ||
| </note> | </note> | ||
| - | <note tip> | ||
| - | Pentru unele distribuții (ex. ubuntu), e posibil să trebuiască să puneți flag-ul "-lcrypti" la compilare, la finalul comenzii. Astfel, ar trebui să aveți ceva de genul: | ||
| - | <code> | ||
| - | #gcc -L/usr/local/lib dhe.c -o dhe -lcrypto | ||
| - | </code> | ||
| - | </note> | ||
| - | <hidden>Hidden section | + | Hidden section |
| === Bonus 1 === | === Bonus 1 === | ||
| Line 181: | Line 177: | ||
| </file> | </file> | ||
| - | </hidden> | + | |
| ==== Introduction to Post Quantum Cryptography ==== | ==== Introduction to Post Quantum Cryptography ==== | ||
| Line 266: | Line 262: | ||
| You will find more details for your task in the skeleton code. | You will find more details for your task in the skeleton code. | ||
| - | {{:ic:labs:ex2a_skeleton.txt|}} | + | |
| + | |||
| + | <spoiler Click pentru a vedea ex2a_skeleton.py> | ||
| + | <file python ex2a_skeleton.py> | ||
| + | import math | ||
| + | import random | ||
| + | from typing import List, Tuple | ||
| + | |||
| + | |||
| + | def int2bin(val: int) -> List[int]: | ||
| + | """ | ||
| + | Convert a 4-bit value to binary and return it as a list. | ||
| + | |||
| + | :param val: 4-bit positive value. | ||
| + | |||
| + | :return l: list of the bits obtained when converting value to binary. | ||
| + | """ | ||
| + | l = [0] * (4) | ||
| + | |||
| + | l[0] = val & 0x1 | ||
| + | l[1] = (val & 0x2) >> 1 | ||
| + | l[2] = (val & 0x4) >> 2 | ||
| + | l[3] = (val & 0x8) >> 3 | ||
| + | |||
| + | return l | ||
| + | |||
| + | |||
| + | def generate( | ||
| + | q: int = 97, | ||
| + | s: int = 19, | ||
| + | nr_values: int = 20, | ||
| + | ) -> Tuple[List[int], List[int]]: | ||
| + | """ | ||
| + | Generate the public key vectors A and B. | ||
| + | |||
| + | :param q: Modulus | ||
| + | :param s: Secret key | ||
| + | :param nr_values: Length of vector variables | ||
| + | |||
| + | :return A, B: Public key vectors, each with "nr_values" elements | ||
| + | |||
| + | TODO 1: Generate public key A | ||
| + | A = [a0, a1, ..., an-1] vector with random values. Of course values modulo q. :) | ||
| + | |||
| + | TODO 2: Generate error vector e | ||
| + | e = [e0, e1, ..., en-1] error vector with small errors in interval [1, 4] | ||
| + | |||
| + | TODO 3: Compute public key B | ||
| + | B = [b0, b1, ..., bn-1] with bi = ai * s + ei. Modulo q do not forget.. | ||
| + | |||
| + | TODO 4: Return public keys A, B | ||
| + | """ | ||
| + | |||
| + | # TODO 1: Generate public key "A" | ||
| + | A = ... | ||
| + | |||
| + | # TODO 2: Generate error vector "e" | ||
| + | e = ... | ||
| + | |||
| + | # TODO 3: Compute public key "B" | ||
| + | B = ... | ||
| + | |||
| + | # TODO 4: Return public keys A, B | ||
| + | |||
| + | |||
| + | def encrypt_bit( | ||
| + | A: List[int], | ||
| + | B: List[int], | ||
| + | plain_bit: int, | ||
| + | q: int = 97, | ||
| + | ) -> Tuple[int, int]: | ||
| + | """ | ||
| + | Encrypt one bit using Learning with Errors(LWE). | ||
| + | |||
| + | :param A: Public key | ||
| + | :param B: Public key | ||
| + | :param plain_bit: Plain bit that you want to encrypt | ||
| + | :param q: Modulus | ||
| + | |||
| + | :return: Cipher pair u, v | ||
| + | |||
| + | TODO 1: Generate a list of 5 random indexes with which you will sample values from public keys A and B. | ||
| + | random_sample_index_list = [random_index_1, random_index_2, ..., random_index_5] | ||
| + | A sample for A is A[random_index_i] or for B is B[random_index_i]. | ||
| + | |||
| + | TODO 2: Compute "u" | ||
| + | u = sum of the samples from vector A | ||
| + | Don't forget modulo. | ||
| + | |||
| + | TODO 3: Compute "v" | ||
| + | v = sum of the samples from vector B + floor(q/2) * plain_bit | ||
| + | Don't forget modulo. | ||
| + | |||
| + | TODO 4: Return cipher pair u, v | ||
| + | """ | ||
| + | |||
| + | # The pair (u, v) will be basically the cipher. | ||
| + | u = 0 | ||
| + | v = 0 | ||
| + | |||
| + | # TODO 1: Generate a list of 5 random indexes with which you will sample values from both public keys A and B. | ||
| + | random_sample_index_list = ... | ||
| + | |||
| + | # TODO 2: Compute u | ||
| + | u = ... | ||
| + | |||
| + | # TODO 3: Compute v | ||
| + | v = ... | ||
| + | |||
| + | # TODO Return the cipher pair (u, v) reduced modulo q | ||
| + | |||
| + | |||
| + | def encrypt( | ||
| + | A: List[int], | ||
| + | B: List[int], | ||
| + | number: int, | ||
| + | q: int = 97, | ||
| + | ) -> List[Tuple[int, int]]: | ||
| + | """ | ||
| + | Encrypt a 4-bit number | ||
| + | |||
| + | :param A: Public Key. | ||
| + | :param B: Public Key. | ||
| + | :param number: Number in interval [0, 15] that you want to encrypt. | ||
| + | :param q: Modulus | ||
| + | |||
| + | :return list with the cipher pairs (ui, vi). | ||
| + | """ | ||
| + | # Convert number to binary; you will obtain a list with 4 bits | ||
| + | bit_list = int2bin(number) | ||
| + | |||
| + | # Using the function that you made before, encrypt each bit. | ||
| + | u0, v0 = encrypt_bit(A, B, bit_list[0], q) | ||
| + | u1, v1 = encrypt_bit(A, B, bit_list[1], q) | ||
| + | u2, v2 = encrypt_bit(A, B, bit_list[2], q) | ||
| + | u3, v3 = encrypt_bit(A, B, bit_list[3], q) | ||
| + | |||
| + | return [(u0, v0), (u1, v1), (u2, v2), (u3, v3)] | ||
| + | |||
| + | |||
| + | def decrypt_bit(cipher_pair: Tuple[int, int], s: int = 19, q: int = 97) -> int: | ||
| + | """ | ||
| + | Decrypt a bit using Learning with errors. | ||
| + | |||
| + | :param cipher_pair: Cipher pair (u, v) | ||
| + | :param s: Secret key | ||
| + | :param q: Modulus | ||
| + | |||
| + | TODO 1: Compute the "dec" value with which you will decrypt the bit. | ||
| + | dec = (v - s * u) modulo q | ||
| + | |||
| + | TODO 2: Obtain and return the decrypted bit. | ||
| + | The decrypted bit is 1 if the previously computed "dec" value is bigger than floor(q/2) and 0 otherwise. | ||
| + | |||
| + | :return list with the cipher pairs (ui, vi). | ||
| + | """ | ||
| + | |||
| + | # Extract pair (u, v) from the argument "cipher_pair". | ||
| + | u = cipher_pair[0] | ||
| + | v = cipher_pair[1] | ||
| + | |||
| + | # TODO 1: Compute "dec" variable | ||
| + | dec = ... | ||
| + | |||
| + | # TODO 2: Decrypt bit and return it | ||
| + | # return 0 or 1 | ||
| + | |||
| + | |||
| + | def decrypt( | ||
| + | cipher: List[Tuple[int, int]], | ||
| + | s: int = 19, | ||
| + | q: int = 97, | ||
| + | ) -> List[int]: | ||
| + | """ | ||
| + | Decrypt a 4-bit number from the cipher text pairs (ui, vi). | ||
| + | |||
| + | :param cipher: Cipher text. List with 4 cipher pairs (u, v) corresponding to each encrypted bit | ||
| + | :param s: Secret key | ||
| + | :param q: Modulus | ||
| + | |||
| + | :return plain: List with the 4 decrypted bits. | ||
| + | """ | ||
| + | u1, v1 = cipher[0][0], cipher[0][1] | ||
| + | u2, v2 = cipher[1][0], cipher[1][1] | ||
| + | u3, v3 = cipher[2][0], cipher[2][1] | ||
| + | u4, v4 = cipher[3][0], cipher[3][1] | ||
| + | |||
| + | bit0 = decrypt_bit((u1, v1), s, q) | ||
| + | bit1 = decrypt_bit((u2, v2), s, q) | ||
| + | bit2 = decrypt_bit((u3, v3), s, q) | ||
| + | bit3 = decrypt_bit((u4, v4), s, q) | ||
| + | |||
| + | return [bit3, bit2, bit1, bit0] | ||
| + | |||
| + | |||
| + | def main() -> None: | ||
| + | # Initialize Parameters | ||
| + | q = 97 | ||
| + | s = 19 | ||
| + | nr_values = 20 | ||
| + | print( | ||
| + | f"Initial parameters are:\n" | ||
| + | " modulus={q}\n" | ||
| + | " secret_key={s}\n" | ||
| + | " nr_of_values={nr_values}\n" | ||
| + | ) | ||
| + | |||
| + | # Integer in [0, 15] that you want to encrypt | ||
| + | number_to_encrypt = 10 | ||
| + | print("You want to encrypt number " + str(number_to_encrypt)) | ||
| + | |||
| + | # Generate Step | ||
| + | A, B = generate(q, s, nr_values) | ||
| + | print("\nPublic Keys obtained:") | ||
| + | print("A=", end="") | ||
| + | print(A) | ||
| + | print("B=", end="") | ||
| + | print(B) | ||
| + | |||
| + | # Encrypt Step | ||
| + | cipher = encrypt(A, B, number_to_encrypt, q) | ||
| + | print("\nCipher is ", end="") | ||
| + | print(cipher) | ||
| + | |||
| + | # Decrypt Step | ||
| + | plain = decrypt(cipher, s, q) | ||
| + | print("\nPlain value in binary is ", end="") | ||
| + | print(plain) | ||
| + | |||
| + | # If plain is the representation in binary of "number_to_encrypt" it should be fine but you can check with other numbers. :D | ||
| + | |||
| + | |||
| + | if __name__ == "__main__": | ||
| + | main() | ||
| + | |||
| + | </file> | ||
| + | </spoiler> | ||
| + | |||
| === Exercise 2b) Testing decryption (2p) === | === Exercise 2b) Testing decryption (2p) === | ||
| Easy right? I have encrypted 5 numbers and stored the ciphers in this file. Can you decrypt them with the function you implemented before? | Easy right? I have encrypted 5 numbers and stored the ciphers in this file. Can you decrypt them with the function you implemented before? | ||
| - | {{:ic:labs:ex2b_cipher.txt|}} | + | <spoiler Click pentru a vedea ex2b_cipher.py> |
| + | <file python ex2b_cipher.py> | ||
| + | # Try to decrypt some secret numbers encrypted using the decryption you just implemented. | ||
| + | |||
| + | # The parameters are the same except that I changed the secret key s. :D | ||
| + | # Of course you need the secret key in order to decrypt the numbers but I won't tell it to you because is secret (s=17). | ||
| + | |||
| + | secretnumber1 = [(57, 11), (91, 13), (38, 29), (68, 55)] | ||
| + | secretnumber2 = [(35, 22), (9, 67), (91, 10), (50, 89)] | ||
| + | secretnumber3 = [(51, 52), (51, 8), (76, 90), (90, 89)] | ||
| + | secretnumber4 = [(68, 50), (18, 28), (93, 43), (61, 77)] | ||
| + | secretnumber5 = [(33, 39), (68, 6), (17, 57), (53, 90)] | ||
| + | |||
| + | # Does [number1, number2, number3, number4, number5] make sense? Maybe in hexadecimal ?? | ||
| + | |||
| + | </file> | ||
| + | </spoiler> | ||
| Just for fun: Do these numbers have a meaning? Maybe they form a word? | Just for fun: Do these numbers have a meaning? Maybe they form a word? | ||
| Line 281: | Line 532: | ||
| To do that just take two 4bit numbers that you want to xor, for example 10 and 5 and encrypt them. Before decrypting you just have to take the obtained ciphers(two (u, v) pairs) and just add them (i.e. (u1 + u2, v1 + v2)). For convenience, you can use if you want the functions provided in the file below. | To do that just take two 4bit numbers that you want to xor, for example 10 and 5 and encrypt them. Before decrypting you just have to take the obtained ciphers(two (u, v) pairs) and just add them (i.e. (u1 + u2, v1 + v2)). For convenience, you can use if you want the functions provided in the file below. | ||
| - | {{:ic:labs:xor_then_decrypt.txt|}} | + | |
| + | <spoiler Click pentru a vedea xor_then_decrypt.py> | ||
| + | <file python ex2b_cipher.py> | ||
| + | from ex2a_skeleton import * | ||
| + | |||
| + | |||
| + | def xor_then_decrypt_bit( | ||
| + | cipher_pair1: Tuple[int, int], | ||
| + | cipher_pair2: Tuple[int, int], | ||
| + | s: int = 19, | ||
| + | q: int = 97, | ||
| + | ): | ||
| + | """ | ||
| + | Xor Cipher pairs and then decrypt a bit using Learning with errors. | ||
| + | |||
| + | :param cipher_pair1: First cipher pair (u, v) | ||
| + | :param cipher_pair2: Second cipher pair (u, v) | ||
| + | :param s: Secret key | ||
| + | :param q: Modulus | ||
| + | |||
| + | TODO 1: Compute the "dec" value with which you will decrypt the bit. | ||
| + | dec = ((v_1 - s * u_1) + (v_2 - s * u_2)) % q | ||
| + | |||
| + | TODO 2: Obtain and return the decrypted bit. | ||
| + | The decrypted bit is 1 if the previously computed "dec" value is bigger than floor(q/2) and 0 otherwise. | ||
| + | |||
| + | :return the decrypted bit | ||
| + | """ | ||
| + | |||
| + | # Extract pair (u, v) from the argument "cipher_pair". | ||
| + | u_1 = cipher_pair1[0] | ||
| + | v_1 = cipher_pair1[1] | ||
| + | |||
| + | u_2 = cipher_pair2[0] | ||
| + | v_2 = cipher_pair2[1] | ||
| + | |||
| + | # TODO 1: Compute "dec" variable | ||
| + | |||
| + | # TODO 2: Decrypt bit and return it | ||
| + | |||
| + | |||
| + | def xor_then_decrypt( | ||
| + | cipher1: List[Tuple[int, int]], | ||
| + | cipher2: List[Tuple[int, int]], | ||
| + | s: int = 19, | ||
| + | q: int = 97, | ||
| + | ) -> List[int]: | ||
| + | """ | ||
| + | Bit wise xor the two cipher pairs and the decrypt 4-bit number result. | ||
| + | |||
| + | :param cipher1: Cipher 1. | ||
| + | :param cipher2: Cipher 2. | ||
| + | :param s: Secret key | ||
| + | :param q: Modulus | ||
| + | |||
| + | :return plain: List with the 4 decrypted bits. | ||
| + | """ | ||
| + | u1_1, v1_1 = cipher1[0][0], cipher1[0][1] | ||
| + | u2_1, v2_1 = cipher1[1][0], cipher1[1][1] | ||
| + | u3_1, v3_1 = cipher1[2][0], cipher1[2][1] | ||
| + | u4_1, v4_1 = cipher1[3][0], cipher1[3][1] | ||
| + | |||
| + | u1_2, v1_2 = cipher2[0][0], cipher2[0][1] | ||
| + | u2_2, v2_2 = cipher2[1][0], cipher2[1][1] | ||
| + | u3_2, v3_2 = cipher2[2][0], cipher2[2][1] | ||
| + | u4_2, v4_2 = cipher2[3][0], cipher2[3][1] | ||
| + | |||
| + | bit0 = xor_then_decrypt_bit((u1_1, v1_1), (u1_2, v1_2), s, q) | ||
| + | bit1 = xor_then_decrypt_bit((u2_1, v2_1), (u2_2, v2_2), s, q) | ||
| + | bit2 = xor_then_decrypt_bit((u3_1, v3_1), (u3_2, v3_2), s, q) | ||
| + | bit3 = xor_then_decrypt_bit((u4_1, v4_1), (u4_2, v4_2), s, q) | ||
| + | |||
| + | return [bit3, bit2, bit1, bit0] | ||
| + | |||
| + | |||
| + | def main() -> None: | ||
| + | # TODO 3: Test it on some examples to see if is working properly | ||
| + | ... | ||
| + | |||
| + | |||
| + | if __name__ == "__main__": | ||
| + | main() | ||
| + | |||
| + | </file> | ||
| + | </spoiler> | ||
| Test it on some examples to see if is working properly! | Test it on some examples to see if is working properly! | ||
| Line 290: | Line 626: | ||
| [[https://github.com/IBM/fhe-toolkit-linux/blob/master/GettingStarted.md]] | [[https://github.com/IBM/fhe-toolkit-linux/blob/master/GettingStarted.md]] | ||
| </note> | </note> | ||
| + | </hidden> | ||
| + | |||