This is an old revision of the document!


Laboratorul 01 - Introduction

Python3 Crash Course

Check-out this tutorial here (only for noobs).

Encoding vs Encryption

Encoding

  • Used to transform data into another format.
  • Typically used for data transfer between different systems.
  • The information is NOT kept secret!!!
  • To decode the encoded data you only need to know the algorithm that was used.

Encryption

  • Transform data in order to keep it secret.
  • The algorithm is usually public, the key is secret.
  • Keywords: plaintext, ciphertext
  • Types: Private-Key Encryption vs Public-Key Encryption (upcoming)

During the labs you will often need to convert data from one format to another. The most used data formats are the following:

  • ASCII (text)
  • Binary (01010101)
  • Hexadecimal [0-9a-fA-F]
  • Base64 [a-Az-Z0-9] and '+' and '/'. Base64 usually ends in '=' or '=='. Used A LOT in Web because HTTP is a text transfer protocol.

Finally, here you have some conversion and XOR related functions for different data formats:

utils.py
import base64
 
# CONVERSION FUNCTIONS
 
def _chunks(string, chunk_size):
    for i in range(0, len(string), chunk_size):
        yield string[i:i+chunk_size]
 
 
def hex_2_bin(data):
    return ''.join(f'{int(x, 16):08b}' for x in _chunks(data, 2))
 
 
def str_2_bin(data):
    return ''.join(f'{ord(c):08b}' for c in data)
 
 
def bin_2_hex(data):
    return ''.join(f'{int(b, 2):02x}' for b in _chunks(data, 8))
 
 
def str_2_hex(data):
    return ''.join(f'{ord(c):02x}' for c in data)
 
 
def bin_2_str(data):
    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 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 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 hexxor(a, b):  # xor two hex-strings, trims the longer input
    return ''.join(hex(int(x, 16) ^ int(y, 16))[2:] for (x, y) in zip(_chunks(a, 2), _chunks(b, 2)))
 
 
# BASE64 FUNCTIONS
 
def b64decode(data):
    return bytes_to_string(base64.b64decode(string_to_bytes(data)))
 
 
def b64encode(data):
    return bytes_to_string(base64.b64encode(string_to_bytes(data)))
 
 
# PYTHON3 'BYTES' FUNCTIONS
 
def bytes_to_string(bytes_data):
    return bytes_data.decode()  # default utf-8
 
 
def string_to_bytes(string_data):
    return string_data.encode()  # default utf-8

Exercise #1

Decode the following strings:

C1 = 010101100110000101101100011010000110000101101100011011000110000100100001
C2 = 526f636b2c2050617065722c2053636973736f727321
C3 = WW91IGRvbid0IG5lZWQgYSBrZXkgdG8gZW5jb2RlIGRhdGEu
Bytes in Python

Check out the following examples:

text1 = "Ana are mere"
text2 = b"Ana are mere"
type(text1) # <class 'str'>
type(text2) # <class 'bytes'>

Both texts store basically the same information. The difference is in how the data is internally 'encoded' into 2 different object types. During the labs we will mostly work with str types, but some external libraries may require to transform the data from the string representation to a bytes object.

Python Exercise #2

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”.

C1 = "000100010001000000001100000000110001011100000111000010100000100100011101000001010001100100000101"

C2 = "02030F07100A061C060B1909"

Hail, Caesar!

Exercise #3

In this first recipe we'll see how to encrypt a single letter using Caesar's cipher.

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)]

Create a new file named caesar.py containing the code above. To test the code, open the interpreter and try the following:

linux$ python
>>> from caesar import *
>>> 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')

Python Exercise #1 - Decrypting a letter

Add a caesar_dec function to caesar.py, which decrypts a single letter encrypted using Caesar's cipher.

Python Recipe #2 - Encrypting a string

We'll now expand our function to take string as input. First, a little intro to Python for loops. Some examples:

for i in [1, 2, 3]:
    print i

In this first example, i iterates through the values in list [1, 2, 3]. The code is similar to the classical C code:

for (int i = 1; i <= 3; i++) {
    printf("%d\n", i);
}

To create lists of integers use the range function:

for i in range(3):
    print i

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:

for letter in 'ABCD':
    print letter

We will now use the for instruction to expand our caesar_enc function:

alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def caesar_enc_string(plaintext):
    ciphertext = ''
    for letter in plaintext:
        ciphertext = ciphertext + caesar_enc(letter)
    return ciphertext
 

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:

linux$ python -i caesar.py
>>> test = 'HELLO'
>>> test + 'WORLD'
>>> caesar_enc_string(test)

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:

'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()

Then you can simply run the program, or type the following in a terminal:

python test_caesar.py

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:

def foo(a, b = 3):
    print a, b
>>> foo(1)
>>> foo(1, 2)

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.

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

To test the new functions, try the below:

linux$ python -i caesar.py
>>> caesar_enc_string('HELLO')
>>> caesar_enc_string('HELLO', 0)
>>> caesar_enc_string('HELLO', 1)

Python Exercise #3 - Shift ciphers

Using default parameters, expand your shift cipher decryption functions to support arbitrary keys.

Python Recipe #4 - Format conversions

ic/laboratoare/01.1601378505.txt.gz · Last modified: 2020/09/29 14:21 by acosmin.maria
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0