În acest laborator vom face niște exerciții ce folosesc algoritmul DES și variații ale acestuia discutate la curs. Prezentarea PowerPoint pentru acest laborator poate fi găsită aici. Puteți lucra acest laborator folosind și platforma Google Colab, accesând acest link.
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): """ Transform a byte (8-bit) value into a bitstring """ 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
DESX este definit sub forma DESX( (k1,k2,k3), m) = k1 ⊕ DES(k2, m ⊕ k3). Construiți un atac împotriva DESX care să ruleze într-un timp proporțional cu 2120.
De ce următoarele scheme nu aduc nicio îmbunătățire față de DES? Justificați!
Scopul acestui exercițiu este de a implementa atacul meet-in-the-middle pe 2DES. Porniți de la scheletul de laborator (vezi mai jos) care se folosește de biblioteca pycrypto.
Realizați următoarele sarcini:
Pornind de la schelet, implementați metodele des2_enc si des2_dec folosind double-DES (2DES).
2DES( (k1,k2), m) = DES(k1, DES(k2, m))
Folosiți următoarele ciphertexts
c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32'
c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf'
Decriptați-le folosind cheile:
k1 = 'Smerenie'
k2 = 'Dragoste'
Primul plaintext corespunzator lui c1 este m1='Fericiti cei saraci cu duhul, ca'. Găsiți al doilea plaintext m2 ce îi corespunde lui c2.
Vom considera de asemenea că inițializăm DES-ul cu valorile default (i.e. modul ECB și niciun IV).
Decriptați întregul ciphertext (c1 || c2) cu cheile k1 și k2 folosind 2DES și asigurați-vă că rezultatul dat este mesajul inițial m1||m2.
În ultimul și cel mai important pas vi se dau niște perechi plaintext/ciphertext obținute prin criptarea cu 2DES cu niște chei necunoscute:
m1 = 'Pocainta' (în byte string, i.e. poate fi dat direct ca parametru către pycrypto DES)
c1 = '9f98dbd6fe5f785d' (în hex string, trebuie să îl decodificați mai întâi)
m2 = 'Iertarea'
c2 = '6e266642ef3069c2'
Vi se mai dau și ultimii 6 bytes ale celor 2 chei (care sunt altele decat cele folosite la pașii anteriori):
k1 (last 6 bytes) = 'oIkvH5'
k2 (last 6 bytes) = 'GK4EoU'
Sarcina voastră este să găsiți restul octeților din k1 și k2 aplicând atacul meet-in-the-middle.
Pentru a construi un tabel, recomandăm să folosiți o lista de tupluri unde adăugați perechi de forma (cheie, encripție) ca în următorul exemplu:
tb = [] tb.append(('keyval', 'encval'))
Pentru a sorta tabelul:
tbs = sorted(tb, key=itemgetter(1))
Pentru a realiza căutarea binară în tabel, selectați mai întâi a doua coloană (pentru a căuta după encripție):
tenc = [value for _,value in tbs]
iar apoi folosiți biblioteca bisect (e.g. bisect.bisect_left): https://docs.python.org/2/library/bisect.html
Scheletul de cod:
from utils import * from operator import itemgetter import bisect from Crypto.Cipher import DES def get_index(a, x): """Locate the leftmost value exactly equal to x in list a""" i = bisect.bisect_left(a, x) if i != len(a) and a[i] == x: return i else: return -1 def des_enc(k, m): """ Encrypt a message m with a key k using DES as follows: c = DES(k, m) Args: 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. 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. The parity bits are ignored. Return: The bytestring ciphertext c """ d = DES.new(k, DES.MODE_ECB) c = d.encrypt(m) return c def des_dec(k, c): """ Decrypt a message c with a key k using DES as follows: m = DES(k, c) Args: 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. 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. The parity bits are ignored. Return: The bytestring plaintext m """ d = DES.new(k, DES.MODE_ECB) m = d.decrypt(c) return m def des2_enc(k1, k2, m): # TODO 3.B: implement des2_enc raise NotImplementedError('Not implemented') def des2_dec(k1, k2, c): # TODO 3.B: implement des2_dec raise NotImplementedError('Not implemented') def main(): k1 = 'Smerenie' k2 = 'Dragoste' m1_given = 'Fericiti cei saraci cu duhul, ca' c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' # TODO 3.C: Decrypt c1 and c2 using k1 and k2, and make sure that # des2_dec(k1, k2, c1 || c2) == m1 || m2 # # Note: The code to decrypt c1 is already provided below. You **need** # to decrypt c2 as well. # m1 = bytes_to_string(des2_dec(string_to_bytes(k1), string_to_bytes(k2), bytes.fromhex(c1))) assert m1 == m1_given, f'Expected "{m1_given}", but got "{m1}"' print('ciphertext:', c1) print('plaintext:', m1) print('plaintext in hexa:', str_2_hex(m1)) # TODO 3.D: run meet-in-the-middle attack for the following plaintext/ciphertext m1 = 'Pocainta' c1 = '9f98dbd6fe5f785d' m2 = 'Iertarea' c2 = '6e266642ef3069c2' # NOTE: you only need to search for the first 2 bytes of the each key (i.e., # to find out what are the values for each `?`) k1 = '??oIkvH5' k2 = '??GK4EoU' if __name__ == '__main__': main()