This shows you the differences between two versions of the page.
|
ic:labs:01 [2022/10/03 15:28] razvan.smadu |
ic:labs:01 [2025/02/23 13:24] (current) razvan.smadu [Laboratorul 01 - Introducere] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ===== Laboratorul 01 - Introducere ===== | ===== Laboratorul 01 - Introducere ===== | ||
| + | |||
| + | Puteți lucra acest laborator folosind și platforma Google Colab, accesând [[https://colab.research.google.com/github/ACS-IC-labs/IC-labs/blob/main/labs/lab01/lab1.ipynb|acest]] link, cu excepția exercițiului bonus. Un scurt tutorial pentru utilizarea platformei poate fi găsit [[https://docs.google.com/document/d/1Dcnyv9wTfWJx8CEgnR6OgLbHAO7XD1BXGjwOAMAEmlc/edit|aici]]. | ||
| ==== Python3 Crash Course ==== | ==== Python3 Crash Course ==== | ||
| - | Tutorialul poate fi găsit [[ic:resurse:python|aici]]. | + | Tutorialul poate fi găsit [[ic:resurse:python|aici]]. La laboratoare vom folosi Python 3.10 sau mai nou. |
| ==== Codificare vs Criptare ==== | ==== Codificare vs Criptare ==== | ||
| Line 24: | Line 26: | ||
| * Binar (01010101) | * Binar (01010101) | ||
| * Hexazecimal [0-9a-fA-F] | * Hexazecimal [0-9a-fA-F] | ||
| - | * Base64 [a-zA-Z0-9] împreună cu '+' și '/'. În general, Base64 se termină cu '=' sau '==' reprezentând padding. Este foarte folosit în web deoarece HTTP este un protocol de transfer text. | + | * Base64 [a-zA-Z0-9] împreună cu '+' și '/'. În general, base64 se termină cu '=' sau '==' reprezentând padding. Este foarte folosit în web deoarece HTTP este un protocol de transfer text. |
| Mai jos găsiți câteva funcții utile pentru conversii și operații de XOR pentru date de diferite formate: | Mai jos găsiți câteva funcții utile pentru conversii și operații de XOR pentru date de diferite formate: | ||
| + | <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 | |
| - | def _hex(x): | + | return data |
| - | 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): | |
| - | def str_2_bin(data): | + | yield data[i : i + chunk_size] |
| - | 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) | + | # Conversion functions |
| - | + | ||
| - | def bin_2_str(data): | + | |
| - | return ''.join(chr(int(b, 2)) for b in _chunks(data, 8)) | + | def hex_2_bin(data: str) -> str: |
| - | + | """Converts a hexadecimal string to a binary representation. | |
| - | def hex_2_str(data): | + | |
| - | return ''.join(chr(int(x, 16)) for x in _chunks(data, 2)) | + | Args: |
| - | + | data (str): The hexadecimal string to be converted. It should have an | |
| - | # XOR FUNCTIONS | + | even number of characters and only contain valid hexadecimal digits |
| - | + | (0-9, A-F, a-f). | |
| - | 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)) | + | Returns: |
| - | + | str: The binary representation of the hexadecimal string, where each | |
| - | def bitxor(a, b): # xor two bit-strings, trims the longer input | + | pair of hexadecimal digits is encoded as an 8-bit binary number. |
| - | return ''.join(str(int(x) ^ int(y)) for (x, y) in zip(a, b)) | + | |
| - | + | Examples: | |
| - | def hexxor(a, b): # xor two hex-strings, trims the longer input | + | >>> hex_2_bin("01abcd") |
| - | return ''.join(_hex(int(x, 16) ^ int(y, 16)) for (x, y) in zip(_chunks(a, 2), _chunks(b, 2))) | + | '000000011010101111001101' |
| - | + | >>> hex_2_bin("0a") | |
| - | # BASE64 FUNCTIONS | + | '00001010' |
| - | + | """ | |
| - | def b64decode(data): | + | return "".join(f"{int(x, 16):08b}" for x in _chunks(data, 2)) |
| - | return bytes_to_string(base64.b64decode(string_to_bytes(data))) | + | |
| - | + | ||
| - | def b64encode(data): | + | def bin_2_hex(data: str) -> str: |
| + | """Converts a binary string to a hexadecimal representation. | ||
| + | |||
| + | 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 b64decode(data: str) -> str: | |
| - | def bytes_to_string(bytes_data): | + | """Decodes a base64 encoded string. |
| - | return bytes_data.decode() # default utf-8 | + | |
| - | + | Args: | |
| - | def string_to_bytes(string_data): | + | data (str): The base64 encoded string to be decoded. It should only |
| - | return string_data.encode() # default utf-8 | + | 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> | ||
| == Bytes în Python == | == Bytes în Python == | ||
| Line 92: | Line 342: | ||
| text1 = "Ana are mere" | text1 = "Ana are mere" | ||
| text2 = b"Ana are mere" | text2 = b"Ana are mere" | ||
| - | type(text1) # <class 'str'> | + | print(type(text1)) # <class 'str'> |
| - | type(text2) # <class 'bytes'> | + | print(type(text2)) # <class 'bytes'> |
| </code> | </code> | ||
| Line 118: | Line 368: | ||
| ==== Hail, Caesar! ==== | ==== Hail, Caesar! ==== | ||
| - | Unul dintre cele mai cunoscute și mai simple scheme de criptare este [[https://en.wikipedia.org/wiki/Caesar_cipher|Cifrul lui Cezar]]. Ideea de baza este de a transforma fiecare litera din plaintext prin deplasarea la stânga a poziției literei curente cu trei poziții. Cu alte cuvinte, A devine D, B devine E, C devine F, și așa mai departe. Operația de criptare a unei litere $m$ este definită prin relația $Enc(m) = (m + 3)\ mod\ 26 $. Analog, pentru a decripta un text, trebuie să facem deplasarea la dreapta cu 3 poziții. Deci, operația de decriptare pentru fiecare literă $c$ dintr-un ciphertext este dată de relația $Dec(c) = (c - 3)\ mod\ 26$. | + | Unul dintre cele mai cunoscute și mai simple scheme de criptare este [[https://en.wikipedia.org/wiki/Caesar_cipher|Cifrul lui Cezar]]. Ideea de bază este de a transforma fiecare literă din plaintext prin deplasarea la stânga a poziției literei curente cu trei poziții. Cu alte cuvinte, A devine D, B devine E, C devine F, și așa mai departe. Operația de criptare a unei litere $m$ este definită prin relația $Enc(m) = (m + 3)\ mod\ 26 $. Analog, pentru a decripta un text, trebuie să facem deplasarea la dreapta cu 3 poziții. Deci, operația de decriptare pentru fiecare literă $c$ dintr-un ciphertext este dată de relația $Dec(c) = (c - 3)\ mod\ 26$. |
| === Criptarea unei litere === | === Criptarea unei litere === | ||
| Line 125: | Line 375: | ||
| <code python> | <code python> | ||
| - | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | + | ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| - | def caesar_enc(letter): | + | |
| - | if letter < 'A' or letter > 'Z': | + | |
| - | print("Invalid letter") | + | def caesar_enc(letter: str) -> str: |
| - | return | + | if not "A" <= letter <= "Z": |
| - | else: | + | raise ValueError("Invalid letter") |
| - | return alphabet[(ord(letter) - ord('A') + 3) % len(alphabet)] | + | return ALPHABET[(ord(letter) - ord("A") + 3) % len(ALPHABET)] |
| </code> | </code> | ||
| Line 150: | Line 400: | ||
| Testați următoarele comenzi în consolă: | Testați următoarele comenzi în consolă: | ||
| <code python> | <code python> | ||
| - | >>> print(alphabet) | + | >>> print(ALPHABET) |
| - | >>> len(alphabet) | + | >>> len(ALPHABET) |
| - | >>> alphabet[0] | + | >>> ALPHABET[0] |
| - | >>> ord('A') | + | >>> ord("A") |
| - | >>> ord('D') - ord('A') | + | >>> ord("D") - ord("A") |
| >>> 26 % 26 | >>> 26 % 26 | ||
| >>> 28 % 26 | >>> 28 % 26 | ||
| Line 162: | Line 412: | ||
| Testați funcția de criptare pe câteva exemple: | Testați funcția de criptare pe câteva exemple: | ||
| <code> | <code> | ||
| - | >>> caesar_enc('D') | + | >>> caesar_enc("D") |
| - | >>> caesar_enc('Z') | + | >>> caesar_enc("Z") |
| - | >>> caesar_enc('B') | + | >>> caesar_enc("B") |
| </code> | </code> | ||
| Line 178: | Line 428: | ||
| <code Python> | <code Python> | ||
| - | def caesar_enc_string(plaintext): | + | def caesar_enc_string(plaintext: str) -> str: |
| - | ciphertext = '' | + | ciphertext = "" |
| for letter in plaintext: | for letter in plaintext: | ||
| - | ciphertext = ciphertext + caesar_enc(letter) | + | ciphertext += caesar_enc(letter) |
| return ciphertext | return ciphertext | ||
| </code> | </code> | ||
| Line 189: | Line 439: | ||
| <code Python> | <code Python> | ||
| shell$ python -i caesar.py # run the script in interactive mode | shell$ python -i caesar.py # run the script in interactive mode | ||
| - | >>> test = 'HELLO' | + | >>> test = "HELLO" |
| - | >>> test += 'WORLD' | + | >>> test += "WORLD" |
| >>> caesar_enc_string(test) | >>> caesar_enc_string(test) | ||
| </code> | </code> | ||
| Line 198: | Line 448: | ||
| <file Python test_caesar.py> | <file Python test_caesar.py> | ||
| - | alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ' | + | ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| - | def caesar_enc(letter): | ||
| - | if letter < 'A' or letter > 'Z': | ||
| - | print('Invalid letter') | ||
| - | return | ||
| - | else: | ||
| - | return alphabet[(ord(letter) - ord('A') + 3) % len(alphabet)] | ||
| - | def caesar_enc_string(plaintext): | + | def caesar_enc(letter: str) -> str: |
| - | ciphertext = '' | + | if not 'A' <= letter <= 'Z': |
| + | raise ValueError('Invalid letter') | ||
| + | return ALPHABET[(ord(letter) - ord('A') + 3) % len(ALPHABET)] | ||
| + | |||
| + | |||
| + | def caesar_enc_string(plaintext: str) -> str: | ||
| + | ciphertext = "" | ||
| for letter in plaintext: | for letter in plaintext: | ||
| - | ciphertext = ciphertext + caesar_enc(letter) | + | ciphertext += caesar_enc(letter) |
| return ciphertext | return ciphertext | ||
| - | def main(): | + | |
| - | m = 'BINEATIVENIT' | + | def main() -> None: |
| + | m = "BINEATIVENIT" | ||
| c = caesar_enc_string(m) | c = caesar_enc_string(m) | ||
| print(c) | print(c) | ||
| - | | + | |
| if __name__ == "__main__": | if __name__ == "__main__": | ||
| main() | main() | ||
| Line 241: | Line 493: | ||
| <code Python> | <code Python> | ||
| - | def caesar_enc(letter, k = 3): | + | def caesar_enc(letter: str, k: int = 3) -> str: |
| - | if letter < 'A' or letter > 'Z': | + | if not "A" <= letter <= "Z": |
| - | print('Invalid letter') | + | raise ValueError("Invalid letter") |
| - | return None | + | return ALPHABET[(ord(letter) - ord("A") + k) % len(ALPHABET)] |
| - | else: | + | |
| - | return alphabet[(ord(letter) - ord('A') + k) % len(alphabet)] | + | |
| - | def caesar_enc_string(plaintext, k = 3): | + | |
| - | ciphertext = '' | + | def caesar_enc_string(plaintext: str, k: int = 3) -> str: |
| + | ciphertext = "" | ||
| for letter in plaintext: | for letter in plaintext: | ||
| - | ciphertext = ciphertext + caesar_enc(letter, k) | + | ciphertext += caesar_enc(letter, k) |
| return ciphertext | return ciphertext | ||
| </code> | </code> | ||
| Line 259: | Line 510: | ||
| <code Python> | <code Python> | ||
| shell$ python -i caesar.py | shell$ python -i caesar.py | ||
| - | >>> caesar_enc_string('HELLO') # use the default value for k | + | >>> caesar_enc_string("HELLO") # use the default value for k |
| - | >>> caesar_enc_string('HELLO', 0) # pass the key as a positional argument | + | >>> caesar_enc_string("HELLO", 0) # pass the key as a positional argument |
| - | >>> caesar_enc_string('HELLO', k=1) # pass the key as a keyword (named) argument | + | >>> caesar_enc_string("HELLO", k=1) # pass the key as a keyword (named) argument |
| </code> | </code> | ||
| Line 320: | Line 571: | ||
| python mtp.py <ciphertexts filename> # OR python3 mtp.py <ciphertexts filename> | python mtp.py <ciphertexts filename> # OR python3 mtp.py <ciphertexts filename> | ||
| </code> | </code> | ||
| + | |||
| + | <note info> | ||
| + | `<ciphertexts filename>` trebuie să conțină cele 10 texte cifrate de mai sus. | ||
| + | </note> | ||
| <note info> | <note info> | ||