If you have questions about any of these rules, email your instructor and ask about it.
pip3 install pycryptodomex --user
project.py that contains the following functions that
implement the ECB mode operations: For your convenience, I am giving you the
template.
# project.py
# name: ...
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad
from Cryptodome.Util.Padding import unpad
# read in_plain_file, encrypt the data, and store the ciphertext in out_cipher_file
def encrypt_ecb(in_plain_file, out_cipher_file, key):
# do something
# read in_cipher_file, decrypt the ciphertext, and store the plaintext in out_plain_file
def decrypt_ecb(in_cipher_file, out_plain_file, key):
# do something
# read normal_bmp_file and in_cipher_file, fix the header in the ciphertext and
# store the results in out_cipher_bmp_file
def fix_bmp_header(normal_bmp_file, in_cipher_file, out_cipher_bmp_file):
# do something
pip3 install pycryptodomex --user
Note that documentation uses "Crypto.Cipher". However, in this project, you need to use "Cryptodome.Cipher". For the sake of backward compatability, the same package is provided under two different names:
pad() and unpad().
fix_bmp_header, as shown in the lecture
notes, should copy the bmp header in the normal_bmp_file
into out_cipher_bmp_file.
..._file should have string type containing
a file name.
key should have
bytes type.
#!/usr/bin/python3
# test_part1.py
from project import *
key = bytes.fromhex("00112233445566778899aabbccddeeff")
encrypt_ecb("pic_original.bmp", "prj_ecb.bin", key)
decrypt_ecb("prj_ecb.bin", "prj_dec.bmp", key)
fix_bmp_header("pic_original.bmp", "prj_ecb.bin", "prj_ecb.bmp")
$ ./test_part1.py $ md5sum pic_original.bmp b62c61f912f1cb7037762d5fddcf782b pic_original.bmp $ md5sum prj_ecb.bin 36b89a0993197a0447a08e2c8722c675 prj_ecb.bin $ md5sum prj_dec.bmp b62c61f912f1cb7037762d5fddcf782b prj_dec.bmp $ md5sum prj_ecb.bmp 3a8b8dec89ef06b882d06565773ab3e3 prj_ecb.bmp $ eog prj_ecb.bmp &
pic_original.bmp from the lecture on Modes of
Operation.
#!/usr/bin/python3
# test_part2.py
import project
key = bytes.fromhex("00112233445566778899aabbccddeeff")
iv = bytes.fromhex("000102030405060708090a0b0c0d0e0f")
project.encrypt_cbc("pic_original.bmp", "prj_cbc.bin", key, iv)
project.decrypt_cbc("prj_cbc.bin", "prj_cbc_dec.bmp", key, iv)
ctr = iv
project.encrypt_ctr("pic_original.bmp", "prj_ctr.bin", key, ctr)
project.decrypt_ctr("prj_ctr.bin", "prj_ctr_dec.bmp", key, ctr)
ctr to be specified with two 8-byte objects: nonce and
initial_value.
project.encrypt_ctr, split the input parameterctr into
nonce and initial_value, and then feed them
when creating a Cipher object.
counter but initial_value
when creating a Cipher object (read the documentation
carefully and understand the difference between the two).
counter), you have to explicitly specify the
parameter names (e.g., using nonce = ) when you feed the
arguments.
eog to see if the decrypted bmp is the same as the original.
$ ./test_part2.py $ md5sum prj_cbc.bin ef302657d9d8c602ce87d8b979ef72d1 prj_cbc.bin $ md5sum prj_cbc_dec.bmp b62c61f912f1cb7037762d5fddcf782b prj_cbc_dec.bmp $ md5sum prj_ctr.bin 7901dcdfc7c7b8ef37a8359d8206d477 prj_ctr.bin $ md5sum prj_ctr_dec.bmp b62c61f912f1cb7037762d5fddcf782b prj_ctr_dec.bmp
>>> from Cryptodome.Util.Padding import pad
>>> blk_size = 16
>>> pad(b"0123456789", blk_size)
b'0123456789\x06\x06\x06\x06\x06\x06'
>>> pad(b"0123456789a", blk_size)
b'0123456789a\x05\x05\x05\x05\x05'
>>> pad(b"0123456789ab", blk_size)
b'0123456789ab\x04\x04\x04\x04'
>>> pad(b"0123456789abc", blk_size)
b'0123456789abc\x03\x03\x03'
>>> pad(b"0123456789abcd", blk_size)
b'0123456789abcd\x02\x02'
>>> pad(b"0123456789abcde", blk_size)
b'0123456789abcde\x01'
>>> pad(b"0123456789abcdef", blk_size)
b'0123456789abcdef\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'
>>> pad(b"0123456789abcdefxxxx", blk_size)
b'0123456789abcdefxxxx\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
As you see from the above sample runs, the padding algorithm works as follows
(read carefully):
pad2 and unpad2) that
implement PKCS#5. Of course, you should use bytes or
bytearray directly instead of calling use the pad/unpad function
in Cryptodome.
Carefully look at the following sample runs. In particular, your
unpad2 function should output "padding error!" if the padded data
has an incorrect format.
>>> import project
>>> padded = project.pad2(b"0123456789")
>>> padded
b'0123456789\x06\x06\x06\x06\x06\x06'
>>> padded = project.pad2(b"0123456789abcdef")
>>> padded
b'0123456789abcdef\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'
>>> project.unpad2(padded)
b'0123456789abcdef'
>>> data = bytes([i for i in range(37)])
>>> data
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$'
>>> unpadded = project.unpad2(data) # the length of data is not a multiple of 16
padding error!
>>> print(unpadded)
None
>>> padded = project.pad2(data)
>>> padded
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$
\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
>>> len(padded)
48
>>> unpadded = project.unpad2(padded)
>>> print(unpadded)
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$'
>>> padded2 = bytearray(padded)
>>> padded2[46] = 1 # corruptting the padding
>>> padded2 = bytes(padded2) # padded2 now contains an incorrect padding
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$
\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x01\x0b'
>>> unpadded = project.unpad2(padded2)
padding error!
>>> print(unpadded)
None
One may argue that if the plaintext does not repeat unlike
pic_original.bmp, it may be secure to the same IV (or CTR). Unfortunately,
we show that that is not the case.
Let us look at the CTR mode. Assume that the attacker gets hold of a plaintext
(P) and a ciphertext (C), can he/she decrypt other encrypted messages if the
CTR is always the same?
P: 546869732069732061206b6e6f776e206d65737361676521 C: ed6b6ee550650f5b9bb699678e244260c3bc87ec8a3172d7 target: d26d68e11e2c0c179bff9c7d842b5860cfad9fbfa80245fc
|
To do:
What is the underlying message for the ciphertext
Tips
|
|
(Warning: This part is probably the most challenging in the entire project. If you're stuck, you may want to move on Parts 6 and 7 first, and then come back to this part.)
From the previous task, we now know that IVs cannot repeat. Another important requirement on IV is that IVs need to be unpredictable for many schemes, i.e., IVs need to be randomly generated.
In this task, we will see what is going to happen if IVs are predictable.
Assume that Bob just sent out an encrypted message, and Eve knows that its content is either Yes or No; Eve can see the ciphertext and the IV used to encrypt the message, but since the encryption algorithm AES is quite strong, Eve has no idea what the actual content is.
However, since Bob uses predictable IVs, Eve knows exactly what IV Bob is going to use next. A good cipher should not only tolerate the known-plaintext attack described previously, it should also tolerate the chosen-plaintext attack. Unfortunately, the IVs he generates are not random, and they can always be predictable.
To emulate the above scenario, you are given part5_py.txt.
Note: Every time you run part5.py, newly chosen plaintext,
key, and iv will be used.
|
Sample run$ ./part5.py iv: db84aee3ccc1205528e9f831b840c195 ct: a3cc08dcf9b18f8e994f491875afdbf1 iv: d8afdd03531eeec72c00b6b3b0c3ec5c pt (hex): 0d96b89f15a1f7b5fe140779e556cacd ct: 521e66a59681f1ea9facf8c8c84bf3b5df9f73b0660d667e13ab503ec727f1dd iv: 5e4fd49d68d4c580a60e6fddaf74b194 pt (hex): 121234 ct: cdfbe11fdb520406d241a7f0f16d3faf iv: aca862411947b4da22560ae8026c6f05 pt (hex): open The secret was: b'No' |
Tips
|
|
iv and ct of the target ciphertext
eog to see the decrypted bmp
files. Check how the corruption affected the decryption for each mode.
(Submit the screenshots).
Follow the diagram by supposing that the first ciphertext block is corrupted.
$ ./part7.py The target ciphertext is iv: 81109c7dff291f6c57f0d6479a334ac8 ct: c892dfdcdaead56d88fbfebac7142442c0fdc499ff0694983d4cd251afd32fb0da6595f66d3e5fb091b7a0f0f18ad9b5 ============= Menu: 0 (encrypt), 1 (decrypt), 2 (open): 0 msg to encrypt in a hexstring format: 11223344aabb iv: a681a0c60ae6fab05f9f1762d3de9ed5 ct: 36be519e0bf9a9fe84e3d51323925195 ============= Menu: 0 (encrypt), 1 (decrypt), 2 (open): 1 iv (in hexstring): a681a0c60ae6fab05f9f1762d3de9ed5 ct (in hexstring): 36be519e0bf9a9fe84e3d51323925195 decryption is: b'\x11"3D\xaa\xbb' ============= Menu: 0 (encrypt), 1 (decrypt), 2 (open): 1 iv (in hexstring): 81109c7dff291f6c57f0d6479a334ac8 ct (in hexstring): c892dfdcdaead56d88fbfebac7142442c0fdc499ff0694983d4cd251afd32fb0da6595f66d3e5fb091b7a0f0f18ad9b5 Cannot decrypt the target ciphertext! ============= Menu: 0 (encrypt), 1 (decrypt), 2 (open): 1 iv (in hexstring): a7bdcc39485abc97f1e20e372e08bfa1 ct (in hexstring): 1216c53c503147f45eb6e970a90ba959 decryption is: b'Hello World' ============= Menu: 0 (encrypt), 1 (decrypt), 2 (open): 2 The message was: b"Don't use ECB. It's not IND-CPA secure"
b"Don't use ECB. It's not IND-CPA secure" b"Show that CBC is not IND-CCA secure!!!"
~/bin/submit -c=IT430 -p=project project_report.doc project.py