This shows you the differences between two versions of the page.
ac:laboratoare:01 [2017/09/25 20:45] dan.dragan |
ac:laboratoare:01 [2024/12/13 10:54] (current) dimitrie.valu |
||
---|---|---|---|
Line 1: | Line 1: | ||
===== Lab 01 - Introduction ===== | ===== Lab 01 - Introduction ===== | ||
- | ==== Python Crash Course ==== | + | This laboratory will serve as an introduction to Python and the Caesar cipher. To solve the lab, open [[https://colab.research.google.com/drive/18Zq4zPlsEWWasOP8fKdOqo9aKFtlYMnW?usp=sharing|this Colab notebook]] and copy it into your own drive for persistence. Do so by going to ''%%File%%'' -> ''%%Save a copy in Drive%%''. |
- | === Python Interactive Mode === | + | <hidden> |
- | + | The solution for the last exercise is: | |
- | Python is an easy to learn interactive programming language. Throughout this lab, we'll be using Python 2.7. | + | |
- | + | ||
- | You can run Python code in two ways: | + | |
- | * by writing a source file and running it (eventually, with some command line arguments) | + | |
- | * by entering commands in interactive mode | + | |
- | + | ||
- | To start interactive mode, simply open a terminal window and run ''python'': | + | |
- | + | ||
- | <code> | + | |
- | linux$ python | + | |
- | </code> | + | |
- | + | ||
- | You can now write Python code just as you would in an actual program: | + | |
- | + | ||
- | <code> | + | |
- | >>> print "Hello, SASC" | + | |
- | </code> | + | |
- | + | ||
- | To quit interactive mode either press ''Ctrl+D'' or use: | + | |
- | + | ||
- | <code> | + | |
- | >>> quit() | + | |
- | </code> | + | |
- | + | ||
- | Interactive mode is useful when you want to quickly try something out (for example, testing decryption for a certain key) without going through the hassle of writing a new source file and running it. | + | |
- | + | ||
- | + | ||
- | === Python Recipe #1 - Hail, Caesar! === | + | |
- | + | ||
- | In Python, indentation is important; it is used to organize code in blocks. For example, the following C code: | + | |
- | + | ||
- | <code C> | + | |
- | int foo(int a, int b) { | + | |
- | int x; | + | |
- | if (a > b) { | + | |
- | x = a; | + | |
- | } else { | + | |
- | x = b; | + | |
- | } | + | |
- | return x | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | is (approximately) the same as the following in Python: | + | |
- | + | ||
- | <code Python> | + | |
- | def foo(a, b): | + | |
- | if a > b: | + | |
- | x = a | + | |
- | else: | + | |
- | x = b | + | |
- | return x | + | |
- | </code> | + | |
- | + | ||
- | In this first recipe we'll see how to encrypt a single letter using Caesar's cipher. | + | |
- | + | ||
- | <code Python> | + | |
- | 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)] | + | |
- | </code> | + | |
- | + | ||
- | Create a new file named caesar.py containing the code above. To test the code, open the interpreter and try the following: | + | |
<code python> | <code python> | ||
- | linux$ python | + | C1 = "000100010001000000001100000000110001011100000111000010100000100100011101000001010001100100000101" |
- | >>> from caesar import * | + | C2 = "02030F07100A061C060B1909" |
- | >>> print alphabet | + | |
- | >>> alphabet[0] | + | |
- | >>> ord('A') | + | |
- | >>> len(alphabet) | + | |
- | >>> ord('D') - ord('A') | + | |
- | >>> 28 % 26 | + | |
- | >>> -1 % 26 | + | |
- | >>> caesar_enc('D') | + | |
- | >>> caesar_enc('Z') | + | |
- | >>> caesar_enc('B') | + | |
- | </code> | + | |
- | === Python Exercise #1 - Decrypting a letter === | + | key = "abcdefghijkl" |
- | Add a ''caesar_dec'' function to ''caesar.py'', which decrypts a single letter encrypted using Caesar's cipher. | + | |
- | === Python Recipe #2 - Encrypting a string === | + | # Solve here |
+ | P1 = bin_2_str(bitxor(str_2_bin(key), C1)) | ||
+ | print(P1) | ||
- | We'll now expand our function to take string as input. First, a little intro to Python ''for'' loops. Some examples: | + | P2 = bin_2_str(bitxor(str_2_bin(key), hex_2_bin(C2))) |
- | + | print(P2) | |
- | <code python> | + | |
- | for i in [1, 2, 3]: | + | |
- | print i | + | |
</code> | </code> | ||
- | In this first example, ''i'' iterates through the values in list ''[1, 2, 3]''. The code is similar to the classical **C** code: | + | The output should be: |
- | + | ||
- | <code C> | + | |
- | for (int i = 1; i <= 3; i++) { | + | |
- | printf("%d\n", i); | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | To create lists of integers use the ''range'' function: | + | |
- | + | ||
- | <code python> | + | |
- | for i in range(3): | + | |
- | print i | + | |
- | </code> | + | |
- | + | ||
- | Execute the code above in the interpreter. Using the range function with two parameters, change the above to print integers 1 through 10. | + | |
- | + | ||
- | In Python, ''for'' can iterate through multiple types of objects, including strings. Try the below: | + | |
- | + | ||
- | <code python> | + | |
- | for letter in 'ABCD': | + | |
- | print letter | + | |
- | </code> | + | |
- | + | ||
- | We will now use the ''for'' instruction to expand our ''caesar_enc'' function: | + | |
- | + | ||
- | <code Python> | + | |
- | alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ' | + | |
- | def caesar_enc_string(plaintext): | + | |
- | ciphertext = '' | + | |
- | for letter in plaintext: | + | |
- | ciphertext = ciphertext + caesar_enc(letter) | + | |
- | return ciphertext | + | |
- | + | ||
- | </code> | + | |
- | + | ||
- | Test the above by starting a new interpreter; this time, we'll skip the ''from caesar import *'' part by running the source file before starting interactive mode: | + | |
- | + | ||
- | <code Python> | + | |
- | linux$ python -i caesar.py | + | |
- | >>> test = 'HELLO' | + | |
- | >>> test + 'WORLD' | + | |
- | >>> caesar_enc_string(test) | + | |
- | </code> | + | |
- | + | ||
- | Another way to run things, which can be very useful in general is to use a main() function and write your program script as follows: | + | |
- | + | ||
- | <code Python 'test_caesar.py'> | + | |
- | import sys | + | |
- | import random | + | |
- | import string | + | |
- | import operator | + | |
- | + | ||
- | 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): | + | |
- | ciphertext = '' | + | |
- | for letter in plaintext: | + | |
- | ciphertext = ciphertext + caesar_enc(letter) | + | |
- | return ciphertext | + | |
- | + | ||
- | def main(): | + | |
- | m = 'BINEATIVENIT' | + | |
- | c = caesar_enc_string(m) | + | |
- | print c | + | |
- | + | ||
- | + | ||
- | if __name__ == "__main__": | + | |
- | main() | + | |
- | </code> | + | |
- | + | ||
- | Then you can simply run the program, or type the following in a terminal: | + | |
- | <code Python> | + | |
- | python test_caesar.py | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | === Python Exercise #2 - Decrypting a string === | + | |
- | + | ||
- | Add the corresponding ''caesar_dec_string'' function. | + | |
- | + | ||
- | === Python Recipe #3 - Shift ciphers === | + | |
- | + | ||
- | Python allows passing default values to parameters: | + | |
- | + | ||
- | <code Python> | + | |
- | def foo(a, b = 3): | + | |
- | print a, b | + | |
- | </code> | + | |
- | <code Python> | + | |
- | >>> foo(1) | + | |
- | >>> foo(1, 2) | + | |
- | </code> | + | |
- | + | ||
- | We can use default parameter values to expand our ''caesar_enc'' function to take the key as an additional parameter, without breaking compatibility with our previous code. | + | |
- | + | ||
- | <code Python> | + | |
- | def caesar_enc(letter, k = 3): | + | |
- | if letter < 'A' or letter > 'Z': | + | |
- | print 'Invalid letter' | + | |
- | return None | + | |
- | else: | + | |
- | return alphabet[(ord(letter) - ord('A') + k) % len(alphabet)] | + | |
- | + | ||
- | def caesar_enc_string(plaintext, k = 3): | + | |
- | ciphertext = '' | + | |
- | for letter in plaintext: | + | |
- | ciphertext = ciphertext + caesar_enc(letter, k) | + | |
- | return ciphertext | + | |
- | </code> | + | |
- | + | ||
- | To test the new functions, try the below: | + | |
- | <code Python> | + | |
- | linux$ python -i caesar.py | + | |
- | >>> caesar_enc_string('HELLO') | + | |
- | >>> caesar_enc_string('HELLO', 0) | + | |
- | >>> caesar_enc_string('HELLO', 1) | + | |
- | </code> | + | |
- | + | ||
- | + | ||
- | === Python Exercise #3 - Shift ciphers === | + | |
- | + | ||
- | Using default parameters, expand your shift cipher decryption functions to support arbitrary keys. | + | |
- | + | ||
- | === Python Recipe #4 - Format conversions === | + | |
- | + | ||
- | <code Python> | + | |
- | import binascii | + | |
- | + | ||
- | 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 hexxor(a, b): # xor two hex strings (trims the longer input) | + | |
- | ha = a.decode('hex') | + | |
- | hb = b.decode('hex') | + | |
- | return "".join([chr(ord(x) ^ ord(y)).encode('hex') for (x, y) in zip(ha, hb)]) | + | |
- | + | ||
- | 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 str2bin(ss): | + | |
- | """ | + | |
- | Transform a string (e.g. 'Hello') into a string of bits | + | |
- | """ | + | |
- | bs = '' | + | |
- | for c in ss: | + | |
- | bs = bs + bin(ord(c))[2:].zfill(8) | + | |
- | return bs | + | |
- | + | ||
- | def hex2bin(hs): | + | |
- | """ | + | |
- | Transform a hex string (e.g. 'a2') into a string of bits (e.g.10100010) | + | |
- | """ | + | |
- | bs = '' | + | |
- | for c in hs: | + | |
- | bs = bs + bin(int(c,16))[2:].zfill(4) | + | |
- | return bs | + | |
- | + | ||
- | def bin2hex(bs): | + | |
- | """ | + | |
- | Transform a bit string into a hex string | + | |
- | """ | + | |
- | return hex(int(bs,2))[2:-1] | + | |
- | + | ||
- | def byte2bin(bval): | + | |
- | """ | + | |
- | Transform a byte (8-bit) value into a bitstring | + | |
- | """ | + | |
- | return bin(bval)[2:].zfill(8) | + | |
- | + | ||
- | def str2int(ss): | + | |
- | """ | + | |
- | Transform a string (e.g. 'Hello') into a (long) integer by converting | + | |
- | first to a bistream | + | |
- | """ | + | |
- | bs = str2bin(ss) | + | |
- | li = int(bs, 2) | + | |
- | return li | + | |
- | + | ||
- | def int2hexstring(bval): | + | |
- | """ | + | |
- | Transform an int value into a hexstring (even number of characters) | + | |
- | """ | + | |
- | hs = hex(bval)[2:] | + | |
- | lh = len(hs) | + | |
- | return hs.zfill(lh + lh%2) | + | |
- | + | ||
- | def bin2str(bs): | + | |
- | """ | + | |
- | Transform a binary srting into an ASCII string | + | |
- | """ | + | |
- | n = int(bs, 2) | + | |
- | return binascii.unhexlify('%x' % n) | + | |
- | </code> | + | |
- | + | ||
- | === Python Exercise #4 - Format conversions === | + | |
- | + | ||
- | Find the plaintext messages for the following ciphertexts knowing that the cipher is the XOR operation (ciphertext = plaintext XOR key) and the key is "abcdefghijkl". | + | |
<code> | <code> | ||
- | C1 = "000100010001000000001100000000110001011100000111000010100000100100011101000001010001100100000101" | + | programatori |
- | + | calculatoare | |
- | C2 = "02030F07100A061C060B1909" | + | |
</code> | </code> | ||
- | |||
- | |||
- | <hidden> | ||
- | The solution is {{:ic:laboratoare:lab1_sol.zip|here}}. | ||
</hidden> | </hidden> |