This shows you the differences between two versions of the page.
|
ic:laboratoare:05 [2017/11/10 18:00] marios.choudary |
ic:laboratoare:05 [2020/11/04 14:00] (current) philip.dumitru [Exercise 3 (5p)] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ===== Laboratorul 05 - DES ===== | ===== Laboratorul 05 - DES ===== | ||
| + | In this lab we'll do some exercises with DES and some of its variants, as we discussed in the last lecture [[https://drive.google.com/file/d/1Fjybv6k5QudRB1bkAi5shVUGlUyTO_0U/view|here]]. | ||
| - | In this lab we'll do some exercises with DES and some of its variants, as we discussed in the last lecture: | + | <file python utils.py> |
| - | http://cs.curs.pub.ro/2014/pluginfile.php/13095/mod_resource/content/2/sasc_curs4_5.pdf | + | 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 byte_2_bin(bval): | ||
| + | return bin(bval)[2:].zfill(8) | ||
| + | |||
| + | def _hex(x): | ||
| + | return format(x, '02x') | ||
| + | |||
| + | 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)) 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 | ||
| + | |||
| + | # THIS ONE IS NEW | ||
| + | def hex_2_bytes(hex_data): | ||
| + | return bytes.fromhex(hex_data) # default utf-8 | ||
| + | </file> | ||
| ==== Exercise 1 (2p) ==== | ==== Exercise 1 (2p) ==== | ||
| Line 21: | Line 80: | ||
| The goal of this exercise is to implement the meet-in-the-middle attack on double DES. | The goal of this exercise is to implement the meet-in-the-middle attack on double DES. | ||
| - | For this, you are given a starter code (see below), implemented using the Pycrypto library. | + | For this, you are given a starter code (see below), implemented using the pycrypto library. |
| Perform the following tasks: | Perform the following tasks: | ||
| Line 27: | Line 86: | ||
| === A. Install the pycrypto library === | === A. Install the pycrypto library === | ||
| - | See https://pypi.python.org/pypi/pycrypto. | + | <hidden>this is outdated: https://pypi.python.org/pypi/pycrypto.</hidden> |
| + | See https://pypi.org/project/pycryptodome/ | ||
| === B. Implement 2DES === | === B. Implement 2DES === | ||
| - | Starting from the starter code (see below), write methods to encrypt and decrypt using double-DES (2DES), defined as follows: | + | Starting from the starter code (see below), write methods to encrypt and decrypt using double-DES (2DES), defined as follows: 2DES( (k1,k2), m) = DES(k1, DES(k2, m)) |
| - | <quote> 2DES( (k1,k2), m) = DES(k1, DES(k2, m))</quote> | + | |
| === C. Test 2DES === | === C. Test 2DES === | ||
| Line 41: | Line 101: | ||
| c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' | c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' | ||
| - | |||
| - | as hex strings (i.e. you need to decode them to get the actual byte strings to use with DES, e.g. c1.decode('hex')) | ||
| Decrypt them using the following keys: | Decrypt them using the following keys: | ||
| Line 65: | Line 123: | ||
| In this last but most important task, you are given the following ciphertext/plaintext pairs for 2DES with unknown keys: | In this last but most important task, you are given the following ciphertext/plaintext pairs for 2DES with unknown keys: | ||
| - | m1 = 'Pocainta' (in byte string, i.e. can be used directly with Pycrypto DES) | + | m1 = 'Pocainta' (in byte string, i.e. can be used directly with pycrypto DES) |
| c1 = '9f98dbd6fe5f785d' (in hex string, you need to hex-decode) | c1 = '9f98dbd6fe5f785d' (in hex string, you need to hex-decode) | ||
| Line 79: | Line 137: | ||
| Your task is now to find the full keys k1 and k2 by applying the meet-in-the-middle attack over 2DES. | Your task is now to find the full keys k1 and k2 by applying the meet-in-the-middle attack over 2DES. | ||
| - | To build a table, I recommend using a list of tuples, where you add new (key,enc) pairs as follows: | + | To build a table, we recommend using a list of tuples, where you add new (key,enc) pairs as follows: |
| <code> | <code> | ||
| tb = [] | tb = [] | ||
| - | tb.append(('keyval', 'encva')) | + | tb.append(('keyval', 'encval')) |
| </code> | </code> | ||
| Line 100: | Line 158: | ||
| <code python desmitm.py> | <code python desmitm.py> | ||
| - | import sys | + | from utils import * |
| - | import random | + | |
| - | import string | + | |
| from operator import itemgetter | from operator import itemgetter | ||
| - | import time | ||
| import bisect | import bisect | ||
| from Crypto.Cipher import DES | from Crypto.Cipher import DES | ||
| - | |||
| - | 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 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 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 | ||
| - | """ | ||
| - | bv = int(bs,2) | ||
| - | return int2hexstring(bv) | ||
| - | |||
| - | def byte2bin(bval): | ||
| - | """ | ||
| - | Transform a byte (8-bit) value into a bitstring | ||
| - | """ | ||
| - | return bin(bval)[2:].zfill(8) | ||
| - | |||
| - | 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 get_index(a, x): | def get_index(a, x): | ||
| - | 'Locate the leftmost value exactly equal to x in list a' | + | """Locate the leftmost value exactly equal to x in list a""" |
| - | i = bisect.bisect_left(a, x) | + | i = bisect.bisect_left(a, x) |
| - | if i != len(a) and a[i] == x: | + | if i != len(a) and a[i] == x: |
| - | return i | + | return i |
| - | else: | + | else: |
| - | return -1 | + | return -1 |
| def des_enc(k, m): | def des_enc(k, m): | ||
| - | """ | + | """ |
| - | Encrypt a message m with a key k using DES as follows: | + | Encrypt a message m with a key k using DES as follows: |
| - | c = DES(k, m) | + | c = DES(k, m) |
| - | Args: | + | Args: |
| - | m should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') | + | m should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') |
| - | k should be a bytestring of length exactly 8 bytes. | + | k should be a bytestring of length exactly 8 bytes. |
| - | Note that for DES the key is given as 8 bytes, where the last bit of | + | Note that for DES the key is given as 8 bytes, where the last bit of |
| - | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. | + | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. |
| - | The parity bits are ignored. | + | The parity bits are ignored. |
| - | Return: | + | Return: |
| - | The bytestring ciphertext c | + | The bytestring ciphertext c |
| - | """ | + | """ |
| - | d = DES.new(k) | + | d = DES.new(k, DES.MODE_ECB) |
| - | c = d.encrypt(m) | + | c = d.encrypt(m) |
| - | + | return c | |
| - | return c | + | |
| def des_dec(k, c): | def des_dec(k, c): | ||
| - | """ | + | """ |
| - | Decrypt a message c with a key k using DES as follows: | + | Decrypt a message c with a key k using DES as follows: |
| - | m = DES(k, c) | + | m = DES(k, c) |
| - | Args: | + | Args: |
| - | c should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') | + | c should be a bytestring (i.e. a sequence of characters such as 'Hello' or '\x02\x04') |
| - | k should be a bytestring of length exactly 8 bytes. | + | k should be a bytestring of length exactly 8 bytes. |
| - | Note that for DES the key is given as 8 bytes, where the last bit of | + | Note that for DES the key is given as 8 bytes, where the last bit of |
| - | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. | + | each byte is just a parity bit, giving the actual key of 56 bits, as expected for DES. |
| - | The parity bits are ignored. | + | The parity bits are ignored. |
| - | Return: | + | Return: |
| - | The bytestring plaintext m | + | The bytestring plaintext m |
| - | """ | + | """ |
| - | d = DES.new(k) | + | d = DES.new(k, DES.MODE_ECB) |
| - | m = d.decrypt(c) | + | m = d.decrypt(c) |
| - | + | return m | |
| - | return m | + | |
| | | ||
| def main(): | def main(): | ||
| + | # Exercitiu pentru test des2_enc | ||
| + | key1 = 'Smerenie' | ||
| + | key2 = 'Dragoste' | ||
| + | m1_given = 'Fericiti cei saraci cu duhul, ca' | ||
| + | c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | ||
| + | # TODO: implement des2_enc and des2_dec | ||
| + | m1 = des2_dec(key1, key2, hex_2_str(c1)) | ||
| - | # Exercitiu pentru test des2_enc | + | print('ciphertext: ' + c1) |
| - | key1 = 'Smerenie' | + | print('plaintext: ' + m1) |
| - | key2 = 'Dragoste' | + | print('plaintext in hexa: ' + str_2_hex(m1)) |
| - | m1_given = 'Fericiti cei saraci cu duhul, ca' | + | |
| - | c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | + | # TODO: run meet-in-the-middle attack for the following plaintext/ciphertext |
| - | # TODO: implement des2_enc and des2_dec | + | m1 = 'Pocainta' |
| - | m1 = des2_dec(key1, key2, c1.decode('hex')) | + | c1 = '9f98dbd6fe5f785d' # in hex string |
| - | + | m2 = 'Iertarea' | |
| - | print 'ciphertext: ' + c1 | + | c2 = '6e266642ef3069c2' |
| - | print 'plaintext: ' + m1 | + | |
| - | print 'plaintext in hexa: ' + m1.encode('hex') | + | |
| - | + | ||
| - | # TODO: run meet-in-the-middle attack for the following plaintext/ciphertext | + | |
| - | m1 = 'Pocainta' | + | |
| - | c1 = '9f98dbd6fe5f785d' # in hex string | + | |
| - | m2 = 'Iertarea' | + | |
| - | c2 = '6e266642ef3069c2' | + | |
| | | ||
| - | # Note: you only need to search for the first 2 bytes of the each key: | + | # Note: you only need to search for the first 2 bytes of the each key: |
| - | k1 = '??oIkvH5' | + | k1 = '??oIkvH5' |
| - | k2 = '??GK4EoU' | + | k2 = '??GK4EoU' |
| - | + | ||
| if __name__ == "__main__": | if __name__ == "__main__": | ||
| - | main() | + | main() |
| </code> | </code> | ||
| - | <hidden>The solution is {{:ic:laboratoare:lab5_sol.zip|here}}.</hidden> | ||