This shows you the differences between two versions of the page.
ic:labs:05 [2023/10/01 23:24] razvan.smadu [Laboratorul 05 - DES] |
ic:labs:05 [2023/10/09 23:22] (current) razvan.smadu |
||
---|---|---|---|
Line 3: | Line 3: | ||
În acest laborator vom face niște exerciții ce folosesc algoritmul DES și variații ale acestuia discutate la [[https://drive.google.com/file/d/1Fjybv6k5QudRB1bkAi5shVUGlUyTO_0U/view|curs]]. | În acest laborator vom face niște exerciții ce folosesc algoritmul DES și variații ale acestuia discutate la [[https://drive.google.com/file/d/1Fjybv6k5QudRB1bkAi5shVUGlUyTO_0U/view|curs]]. | ||
Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1FU422fCHM24fRnMuzFd0OjhQqT6AQZ3X/view?usp=sharing|aici]]. | Prezentarea PowerPoint pentru acest laborator poate fi găsită [[https://drive.google.com/file/d/1FU422fCHM24fRnMuzFd0OjhQqT6AQZ3X/view?usp=sharing|aici]]. | ||
- | Puteți lucra acest laborator folosind și platforma Google Colab, accesând [[https://colab.research.google.com/drive/1Vi5V7mHYIau6vbGFiQITd2FRzYRT3o1j?usp=sharing|acest]] link. | + | Puteți lucra acest laborator folosind platforma Google Colab, accesând [[https://colab.research.google.com/github/ACS-IC-labs/IC-labs/blob/main/labs/lab05/lab5.ipynb|acest]] link. |
+ | <hidden> | ||
<spoiler Click pentru a vedea utils.py> | <spoiler Click pentru a vedea utils.py> | ||
<file python utils.py> | <file python utils.py> | ||
Line 428: | Line 429: | ||
Scheletul de cod: | Scheletul de cod: | ||
- | <code python desmitm.py> | + | <spoiler Click pentru a vedea lab05.py> |
- | from utils import * | + | <file python lab05.py> |
- | from operator import itemgetter | + | |
import bisect | import bisect | ||
+ | from operator import itemgetter | ||
+ | from typing import List | ||
+ | |||
from Crypto.Cipher import DES | from Crypto.Cipher import DES | ||
+ | from utils import * | ||
- | def get_index(a, x): | + | def get_index(a: List[bytes], x: bytes) -> int: |
- | """Locate the leftmost value exactly equal to x in list a""" | + | """Locate the leftmost value exactly equal to x in list a |
+ | |||
+ | Args: | ||
+ | a (List[bytes]): the list in which to search | ||
+ | x (bytes): the value to be searched | ||
+ | |||
+ | Returns: | ||
+ | int: The leftmost index at which the value is found in the list, | ||
+ | or -1 if not found | ||
+ | """ | ||
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: | ||
Line 444: | Line 457: | ||
- | def des_enc(k, m): | + | def des_enc(k: bytes, m: bytes) -> bytes: |
""" | """ | ||
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: | ||
- | 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 | 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 | each byte is just a parity bit, giving the actual key of 56 bits, as | ||
expected for DES. The parity bits are ignored. | expected for DES. The parity bits are ignored. | ||
+ | |||
+ | Args: | ||
+ | k (str): bytestring of length exactly 8 bytes. | ||
+ | m (str): bytestring containing the message (i.e. a sequence of | ||
+ | characters such as 'Hello' or '\x02\x04') | ||
Return: | Return: | ||
- | The bytestring ciphertext c | + | bytes: The bytestring ciphertext c |
""" | """ | ||
d = DES.new(k, DES.MODE_ECB) | d = DES.new(k, DES.MODE_ECB) | ||
Line 466: | Line 479: | ||
- | def des_dec(k, c): | + | def des_dec(k: bytes, c: bytes) -> bytes: |
""" | """ | ||
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: | ||
- | 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 | 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 | each byte is just a parity bit, giving the actual key of 56 bits, as | ||
expected for DES. The parity bits are ignored. | expected for DES. The parity bits are ignored. | ||
+ | |||
+ | Args: | ||
+ | k (str): bytestring of length exactly 8 bytes. | ||
+ | c (str): bytestring containing the ciphertext (i.e. a sequence of | ||
+ | characters such as 'Hello' or '\x02\x04') | ||
Return: | Return: | ||
- | The bytestring plaintext m | + | bytes: The bytestring plaintext m |
""" | """ | ||
d = DES.new(k, DES.MODE_ECB) | d = DES.new(k, DES.MODE_ECB) | ||
Line 488: | Line 501: | ||
- | def des2_enc(k1, k2, m): | + | def des2_enc(k1: bytes, k2: bytes, m: bytes) -> bytes: |
- | # TODO 3.B: implement des2_enc | + | # TODO B.1: implement des2_enc |
- | raise NotImplementedError('Not implemented') | + | raise NotImplementedError("Not implemented") |
- | def des2_dec(k1, k2, c): | + | def des2_dec(k1: bytes, k2: bytes, c: bytes) -> bytes: |
- | # TODO 3.B: implement des2_dec | + | # TODO B.2: implement des2_dec |
- | raise NotImplementedError('Not implemented') | + | raise NotImplementedError("Not implemented") |
- | def main(): | + | def test_des2() -> None: |
- | k1 = 'Smerenie' | + | k1 = "Smerenie" |
- | k2 = 'Dragoste' | + | k2 = "Dragoste" |
- | m1_given = 'Fericiti cei saraci cu duhul, ca' | + | m1_given = "Fericiti cei saraci cu duhul, ca" |
- | c1 = 'cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32' | + | c1 = "cda98e4b247612e5b088a803b4277710f106beccf3d020ffcc577ddd889e2f32" |
- | c2 = '54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf' | + | c2 = "54826ea0937a2c34d47f4595f3844445520c0995331e5d492f55abcf9d8dfadf" |
- | # TODO 3.C: Decrypt c1 and c2 using k1 and k2, and make sure that | + | # TODO C: Decrypt c1 and c2 using k1 and k2, and make sure that |
# des2_dec(k1, k2, c1 || c2) == m1 || m2 | # des2_dec(k1, k2, c1 || c2) == m1 || m2 | ||
- | # | + | |
- | # Note: The code to decrypt c1 is already provided below. You **need** | + | # TODO C.1: Convert k1, k2, c1, and c2 to bytes. It may make the exercise |
+ | # easier to implement. Use string_to_bytes() for plain texts (i.e., string | ||
+ | # in human-readable format), and bytes.fromhex() for hex strings. | ||
+ | k1 = ... | ||
+ | k2 = ... | ||
+ | c1 = ... | ||
+ | c2 = ... | ||
+ | |||
+ | # NOTE: The code to decrypt c1 is already provided below. You **need** | ||
# to decrypt c2 as well. | # to decrypt c2 as well. | ||
- | # | + | m1 = bytes_to_string(des2_dec(k1, k2, c1)) |
- | 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}"' | assert m1 == m1_given, f'Expected "{m1_given}", but got "{m1}"' | ||
- | print('ciphertext:', c1) | + | print("ciphertext:", c1) |
- | print('plaintext:', m1) | + | print("plaintext:", m1) |
- | print('plaintext in hexa:', str_2_hex(m1)) | + | print("plaintext in hexa:", str_2_hex(m1)) |
- | # TODO 3.D: run meet-in-the-middle attack for the following plaintext/ciphertext | + | # TODO C.2: Decrypt m2 similar to m1. Keep in mind that des_dec() |
- | m1 = 'Pocainta' | + | # returns bytes |
- | c1 = '9f98dbd6fe5f785d' | + | m2 = ... |
- | m2 = 'Iertarea' | + | |
- | c2 = '6e266642ef3069c2' | + | |
- | # NOTE: you only need to search for the first 2 bytes of the each key (i.e., | + | print("ciphertext:", c2) |
- | # to find out what are the values for each `?`) | + | print("plaintext:", m2) |
- | k1 = '??oIkvH5' | + | print("plaintext in hexa:", str_2_hex(m2)) |
- | k2 = '??GK4EoU' | + | |
+ | # TODO C.3: Just to make sure you implemented the task correctly: | ||
+ | # des2_dec(k1, k2, c1 || c2) == m1 || m2 | ||
+ | m12 = ... | ||
+ | assert m12 == m1 + m2, f'Expected "{m12}" to equal "{m1 + m2}"' | ||
- | if __name__ == '__main__': | ||
- | main() | ||
- | </code> | ||
+ | def mitm() -> None: | ||
+ | # TODO D: run meet-in-the-middle attack for the following plaintext/ciphertext | ||
+ | m1 = "Pocainta" | ||
+ | c1 = "9f98dbd6fe5f785d" | ||
+ | m2 = "Iertarea" | ||
+ | c2 = "6e266642ef3069c2" | ||
+ | |||
+ | # TODO D.1: Convert m1, m2, c1, and c2 to bytes. It may make the exercise | ||
+ | # easier to implement. Use string_to_bytes() for plain texts (i.e., string | ||
+ | # in human-readable format), and bytes.fromhex() for hex strings. | ||
+ | m1 = ... | ||
+ | c1 = ... | ||
+ | m2 = ... | ||
+ | c2 = ... | ||
+ | |||
+ | # 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" | ||
+ | |||
+ | # TODO D.2: Generate the table containing (k2, DES(k2, m1)), for every | ||
+ | # possible k2. Use a List[Tuple[bytes, bytes]] for the table (see task | ||
+ | # description above). | ||
+ | |||
+ | # TODO D.3: Sort the table based on the ciphertexts. Extract the ciphertexts | ||
+ | # in a list (see task description above). | ||
+ | |||
+ | # TODO D.4: Perform binary search for all possible k1, such that . | ||
+ | # Save the set of candidate keys (k1, k2) in a list. | ||
+ | |||
+ | # TODO D.5: From the set of candidate keys, print the ones matching | ||
+ | # the second constraint: 2DES(k1, k2, m2) == c2 | ||
+ | |||
+ | |||
+ | def main() -> None: | ||
+ | test_des2() | ||
+ | mitm() | ||
+ | |||
+ | |||
+ | if __name__ == "__main__": | ||
+ | main()</file> | ||
+ | </spoiler> | ||
+ | </hidden> |