This shows you the differences between two versions of the page.
ic:labs:01 [2022/10/03 15:28] razvan.smadu |
ic:labs:01 [2023/10/01 23:03] (current) razvan.smadu [Python3 Crash Course] |
||
---|---|---|---|
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/drive/1G8ycsy34UwwQqJHp2DgL5MtGERWzkhN_|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> |