This shows you the differences between two versions of the page.
ic:labs:03 [2022/10/14 23:59] razvan.smadu |
ic:labs:03 [2023/10/16 20:53] (current) razvan.smadu |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Laboratorul 03 - PRGs ===== | ===== Laboratorul 03 - PRGs ===== | ||
- | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1IfyMkfV6jQDzW5KhS_B6wt_azV31SLjL/view?usp=sharing|aici]]. | + | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1IfyMkfV6jQDzW5KhS_B6wt_azV31SLjL/view?usp=sharing|aici]]. Puteți găsi o scurtă prezentare a noțiunii de avantaje în [[https://drive.google.com/file/d/1F_mtKi0zeAn1xYQs6Wc7IiiVX8ZW6aeG/view?usp=sharing|acest]] PDF. |
- | Puteți lucra acest laborator folosind și platforma Google Colab, accesând [[https://colab.research.google.com/drive/1dT6VieWJ1QeMLkb0UY3bAuKvTnkRmWBq?usp=sharing | + | 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/lab03/lab3.ipynb |
|acest]] link. | |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): | + | def _pad(data: str, size: int) -> str: |
- | for i in range(0, len(string), chunk_size): | + | reminder = len(data) % size |
- | yield string[i:i+chunk_size] | + | if reminder != 0: |
+ | data = "0" * (size - reminder) + data | ||
+ | return data | ||
- | def _hex(x): | ||
- | return format(x, '02x') | ||
- | def hex_2_bin(data): | + | def _chunks(data: str, chunk_size: int) -> Generator[str, None, None]: |
- | return ''.join(f'{int(x, 16):08b}' for x in _chunks(data, 2)) | + | data = _pad(data, chunk_size) |
+ | for i in range(0, len(data), chunk_size): | ||
+ | yield data[i : i + chunk_size] | ||
- | def str_2_bin(data): | ||
- | return ''.join(f'{ord(c):08b}' for c in data) | ||
- | def bin_2_hex(data): | + | def _hex(data: int) -> str: |
- | return ''.join(f'{int(b, 2):02x}' for b in _chunks(data, 8)) | + | return format(data, "02x") |
- | def str_2_hex(data): | ||
- | return ''.join(f'{ord(c):02x}' for c in data) | ||
- | def bin_2_str(data): | + | # Conversion functions |
- | 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 hex_2_bin(data: str) -> str: |
+ | """Converts a hexadecimal string to a binary representation. | ||
- | def strxor(a, b): # xor two strings, trims the longer input | + | Args: |
- | return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)) | + | 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). | ||
- | def bitxor(a, b): # xor two bit-strings, trims the longer input | + | Returns: |
- | return ''.join(str(int(x) ^ int(y)) for (x, y) in zip(a, b)) | + | str: The binary representation of the hexadecimal string, where each |
+ | pair of hexadecimal digits is encoded as an 8-bit binary number. | ||
- | def hexxor(a, b): # xor two hex-strings, trims the longer input | + | Examples: |
- | return ''.join(_hex(int(x, 16) ^ int(y, 16)) for (x, y) in zip(_chunks(a, 2), _chunks(b, 2))) | + | >>> hex_2_bin("01abcd") |
+ | '000000011010101111001101' | ||
+ | >>> hex_2_bin("0a") | ||
+ | '00001010' | ||
+ | """ | ||
+ | return "".join(f"{int(x, 16):08b}" for x in _chunks(data, 2)) | ||
- | # BASE64 FUNCTIONS | ||
- | def b64decode(data): | + | def bin_2_hex(data: str) -> str: |
- | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | + | """Converts a binary string to a hexadecimal representation. |
- | def b64encode(data): | + | 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 hexadecimal representation of the binary string, where each | ||
+ | group of 8 binary digits is encoded as a pair of hexadecimal digits. | ||
+ | |||
+ | Examples: | ||
+ | >>> bin_2_hex("000000011010101111001101") | ||
+ | '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 Latin-1 format. | ||
+ | |||
+ | Returns: | ||
+ | str: The string representation of the byte array or the byte string, | ||
+ | decoded using Latin-1 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> bytes_to_string(b'Hello') | ||
+ | 'Hello' | ||
+ | >>> bytes_to_string(bytearray(b'IC')) | ||
+ | 'IC' | ||
+ | """ | ||
+ | return bytes_data.decode(encoding="raw_unicode_escape") | ||
+ | |||
+ | |||
+ | 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 | ||
+ | Latin-1 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> string_to_bytes('Hello') | ||
+ | b'Hello' | ||
+ | >>> string_to_bytes('IC') | ||
+ | b'IC' | ||
+ | """ | ||
+ | return string_data.encode(encoding="raw_unicode_escape") | ||
+ | |||
+ | |||
+ | # 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 Latin-1 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): | + | def b64decode(data: str) -> str: |
- | return bytes_data.decode() # default utf-8 | + | """Decodes a base64 encoded string. |
- | def string_to_bytes(string_data): | + | Args: |
- | return string_data.encode() # default utf-8 | + | 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 Latin-1 encoding. | ||
+ | |||
+ | Examples: | ||
+ | >>> b64decode("SGVsbG8=") | ||
+ | 'Hello' | ||
+ | >>> b64decode("SUM=") | ||
+ | 'IC' | ||
+ | """ | ||
+ | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | ||
</file> | </file> | ||
+ | </spoiler> | ||
+ | |||
==== Exercițiul 1 (3p) ==== | ==== Exercițiul 1 (3p) ==== | ||
Line 88: | Line 337: | ||
Puteți folosi următorul schelet de cod: | Puteți folosi următorul schelet de cod: | ||
<code python 'ex1_weak_rng.py'> | <code python 'ex1_weak_rng.py'> | ||
+ | from typing import List, Tuple | ||
+ | |||
from utils import * | from utils import * | ||
class WeakRNG: | class WeakRNG: | ||
- | """ Simple class for weak RNG """ | + | """Simple class for weak RNG""" |
- | def __init__(self): | + | def __init__(self) -> None: |
self.rstate = 0 | self.rstate = 0 | ||
self.maxn = 255 | self.maxn = 255 | ||
Line 101: | Line 352: | ||
self.p = 257 | self.p = 257 | ||
- | def init_state(self, rstate): | + | def init_state(self, rstate: int) -> None: |
- | """ Initialise rstate """ | + | """Initialise rstate""" |
self.rstate = rstate # Set this to some value | self.rstate = rstate # Set this to some value | ||
self.update_state() | self.update_state() | ||
- | def update_state(self): | + | def update_state(self) -> None: |
- | """ Update state """ | + | """Update state""" |
self.rstate = (self.a * self.rstate + self.b) % self.p | self.rstate = (self.a * self.rstate + self.b) % self.p | ||
- | def get_prg_byte(self): | + | def get_prg_byte(self) -> int: |
- | """ Return a new PRG byte and update PRG state """ | + | """Return a new PRG byte and update PRG state""" |
b = self.rstate & 0xFF | b = self.rstate & 0xFF | ||
self.update_state() | self.update_state() | ||
Line 117: | Line 368: | ||
- | def main(): | + | def main() -> None: |
# Initialise weak rng | # Initialise weak rng | ||
wr = WeakRNG() | wr = WeakRNG() | ||
Line 123: | Line 374: | ||
# Print ciphertext | # Print ciphertext | ||
- | CH = 'a432109f58ff6a0f2e6cb280526708baece6680acc1f5fcdb9523129434ae9f6ae9edc2f224b73a8' | + | CH = "a432109f58ff6a0f2e6cb280526708baece6680acc1f5fcdb9523129434ae9f6ae9edc2f224b73a8" |
print("Full ciphertext in hexa:", CH) | print("Full ciphertext in hexa:", CH) | ||
# Print known plaintext | # Print known plaintext | ||
- | pknown = 'Let all creation' | + | pknown = "Let all creation" |
nb = len(pknown) | nb = len(pknown) | ||
print("Known plaintext:", pknown) | print("Known plaintext:", pknown) | ||
Line 134: | Line 385: | ||
# Obtain first nb bytes of RNG | # Obtain first nb bytes of RNG | ||
- | gh = hexxor(pkh, CH[0:nb*2]) | + | gh = hexxor(pkh, CH[0 : nb * 2]) |
print(gh) | print(gh) | ||
gbytes = [] | gbytes = [] | ||
for i in range(nb): | for i in range(nb): | ||
- | gbytes.append(ord(hex_2_str(gh[2*i:2*i+2]))) | + | gbytes.append(ord(hex_2_str(gh[2 * i : 2 * i + 2]))) |
print("Bytes of RNG: ") | print("Bytes of RNG: ") | ||
print(gbytes) | print(gbytes) | ||
Line 150: | Line 401: | ||
# TODO 4: Print the full plaintext | # TODO 4: Print the full plaintext | ||
- | p = '' | + | p = ... |
print("Full plaintext is:", p) | print("Full plaintext is:", p) | ||
Line 241: | Line 492: | ||
import random | import random | ||
- | def get_random_string(n): | + | def get_random_string(n: int) -> str: |
- | """ Generate random bit string """ | + | """Generate random bit string""" |
- | return bin(random.getrandbits(n)).lstrip('0b').zfill(n) | + | return bin(random.getrandbits(n)).lstrip("0b").zfill(n) |
</code> | </code> | ||
</note> | </note> | ||
Line 253: | Line 504: | ||
</note> | </note> | ||
+ | <file python ex4.py> | ||
+ | import math | ||
+ | import random | ||
+ | |||
+ | from utils import * | ||
+ | |||
+ | |||
+ | def get_random_string(n: int) -> str: | ||
+ | """Generate random bit string""" | ||
+ | return bin(random.getrandbits(n)).lstrip("0b").zfill(n) | ||
+ | |||
+ | |||
+ | def main() -> None: | ||
+ | N_RUNS = 100 # the number of runs | ||
+ | N_BITS = 1000 # the number of bits to generate | ||
+ | |||
+ | # TODO: implement and run the multiple times (e.g., 100) the | ||
+ | # statistical tests | ||
+ | |||
+ | |||
+ | if __name__ == "__main__": | ||
+ | main() | ||
+ | </file> | ||
==== Exercițiul 5 - Bonus (2p) ==== | ==== Exercițiul 5 - Bonus (2p) ==== | ||
Line 272: | Line 546: | ||
Încercați să implementați aceeași funcționalitate folosind OpenSSL. OpenSSL suportă RC4, dar nu și Salsa20. Folosiți ChaCha20 in loc de Salsa20, aceasta fiind o variantă îmbunătățită a algoritmului. | Încercați să implementați aceeași funcționalitate folosind OpenSSL. OpenSSL suportă RC4, dar nu și Salsa20. Folosiți ChaCha20 in loc de Salsa20, aceasta fiind o variantă îmbunătățită a algoritmului. | ||
+ | |||
+ | </hidden> |