现代密码学 实验二 题目一 题目地址:https://www.mysterytwisterc3.org/en/challenges/level-2/aes-key--encoded-in-the-machine-readable-zone-of-a-european-epassport
1.MTC3 AES key — encoded in the machine readable zone of a European ePassport 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import base64import hashlibfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesdef recover_mrz_character (mrz ): weights = (7 , 3 , 1 ) values = {str (i): i for i in range (10 )} values.update({chr (ord ("A" ) + i): 10 + i for i in range (26 )}) values["<" ] = 0 def checksum (text ): return sum (values[char] * weights[index % 3 ] for index, char in enumerate (text)) % 10 for digit in "0123456789" : candidate = mrz.replace("?" , digit, 1 ) if checksum(candidate[21 :27 ]) == int (candidate[27 ]): return digit raise ValueError("missing MRZ character not found" ) def set_odd_parity (byte ): masked = byte & 0xFE return masked | (0 if bin (masked).count("1" ) % 2 else 1 ) def aes_cbc_decrypt (data, key, iv ): cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() return decryptor.update(data) + decryptor.finalize() def strip_bac_padding (data ): suffix = data.rstrip(b"\x00" ) if suffix.endswith(b"\x01" ): return suffix[:-1 ] return data mrz_partial = "12345678<8<<<1110182<111116?<<<<<<<<<<<<<<<4" recovered_digit = recover_mrz_character(mrz_partial) mrz_full = mrz_partial.replace("?" , recovered_digit, 1 ) mrz_info = mrz_full[:10 ] + mrz_full[13 :20 ] + mrz_full[21 :28 ] k_seed = hashlib.sha1(mrz_info.encode()).digest()[:16 ] raw_key = hashlib.sha1(k_seed + (1 ).to_bytes(4 , "big" )).digest()[:16 ] key_enc = bytes (set_odd_parity(byte) for byte in raw_key) ciphertext = base64.b64decode( "9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jx" "aa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2N" "fNnWFBTXyf7SDI" ) plaintext = strip_bac_padding(aes_cbc_decrypt(ciphertext, key_enc, b"\x00" * 16 )).decode() print (recovered_digit)print (mrz_info)print (key_enc.hex ())print (plaintext)
题目二 题目地址:http://www.cryptopals.com/sets/2
1.Implement PKCS#7 padding 题目地址:https://www.cryptopals.com/sets/2/challenges/9
1 2 3 4 5 6 def pkcs7_pad (data, block_size ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len print (pkcs7_pad(b"YELLOW SUBMARINE" , 20 ))
2.Implement CBC mode 题目地址:https://www.cryptopals.com/sets/2/challenges/10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import base64import requestsfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesdef bytesxor (a, b ): return bytes ([x ^ y for x, y in zip (a, b)]) def aes_ecb_decrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) decryptor = cipher.decryptor() return decryptor.update(data) + decryptor.finalize() def aes_cbc_decrypt (data, key, iv ): out = b"" last = iv for i in range (0 , len (data), 16 ): block = data[i : i + 16 ] out += bytesxor(aes_ecb_decrypt(block, key), last) last = block return out def pkcs7_unpad (data ): return data[: -data[-1 ]] data = base64.b64decode(requests.get("https://cryptopals.com/static/challenge-data/10.txt" ).text) plain = pkcs7_unpad(aes_cbc_decrypt(data, b"YELLOW SUBMARINE" , b"\x00" * 16 )).decode(errors="ignore" ) print ("\n" .join(plain.splitlines()[:4 ]))
3.An ECB/CBC detection oracle 题目地址:https://www.cryptopals.com/sets/2/challenges/11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import randomfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesrandom.seed(20260515 ) def randbytes (n ): return bytes ([random.getrandbits(8 ) for _ in range (n)]) def pkcs7_pad (data, block_size=16 ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len def aes_ecb_encrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) encryptor = cipher.encryptor() return encryptor.update(data) + encryptor.finalize() def bytesxor (a, b ): return bytes ([x ^ y for x, y in zip (a, b)]) def aes_cbc_encrypt (data, key, iv ): data = pkcs7_pad(data) out = b"" last = iv for i in range (0 , len (data), 16 ): block = bytesxor(data[i : i + 16 ], last) last = aes_ecb_encrypt(block, key) out += last return out def detect_ecb (data, block_size=16 ): blocks = [data[i : i + block_size] for i in range (0 , len (data), block_size)] return len (blocks) != len (set (blocks)) def oracle (data ): key = randbytes(16 ) data = randbytes(random.randint(5 , 10 )) + data + randbytes(random.randint(5 , 10 )) if random.randint(0 , 1 ) == 0 : return aes_ecb_encrypt(pkcs7_pad(data), key), "ECB" else : return aes_cbc_encrypt(data, key, randbytes(16 )), "CBC" cnt = 0 for _ in range (100 ): x, mode = oracle(b"A" * 64 ) guess = "ECB" if detect_ecb(x) else "CBC" if guess == mode: cnt += 1 print (cnt)
4.Byte-at-a-time ECB decryption (Simple) 题目地址:https://www.cryptopals.com/sets/2/challenges/12
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import base64import randomfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesrandom.seed(20260515 ) def randbytes (n ): return bytes ([random.getrandbits(8 ) for _ in range (n)]) def pkcs7_pad (data, block_size=16 ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len def aes_ecb_encrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) encryptor = cipher.encryptor() return encryptor.update(data) + encryptor.finalize() def block_size (oracle ): n = len (oracle(b"" )) for i in range (1 , 64 ): m = len (oracle(b"A" * i)) if m > n: return m - n unknown = base64.b64decode( b"Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg" b"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq" b"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg" b"YnkK" ) key = randbytes(16 ) def oracle (data ): return aes_ecb_encrypt(pkcs7_pad(data + unknown), key) bs = block_size(oracle) ans = b"" for _ in range (len (oracle(b"" ))): pad = b"A" * (bs - 1 - len (ans) % bs) block_id = len (ans) // bs target = oracle(pad)[block_id * bs : (block_id + 1 ) * bs] d = {} for i in range (256 ): x = oracle(pad + ans + bytes ([i]))[block_id * bs : (block_id + 1 ) * bs] d[x] = i if target not in d: break ans += bytes ([d[target]]) print (ans.decode().rstrip("\x01" ))
5.ECB cut-and-paste 题目地址:https://www.cryptopals.com/sets/2/challenges/13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import randomfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesrandom.seed(20260515 ) key = bytes ([random.getrandbits(8 ) for _ in range (16 )]) def pkcs7_pad (data, block_size=16 ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len def pkcs7_unpad (data ): return data[: -data[-1 ]] def aes_ecb_encrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) encryptor = cipher.encryptor() return encryptor.update(data) + encryptor.finalize() def aes_ecb_decrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) decryptor = cipher.decryptor() return decryptor.update(data) + decryptor.finalize() def profile_for (email ): email = email.replace("&" , "" ).replace("=" , "" ) return f"email={email} &uid=10&role=user" .encode() def encrypt_profile (email ): return aes_ecb_encrypt(pkcs7_pad(profile_for(email)), key) def decrypt_profile (data ): s = pkcs7_unpad(aes_ecb_decrypt(data, key)).decode() print (s) admin_block = encrypt_profile("A" * 10 + "admin" + chr (11 ) * 11 )[16 :32 ] base = encrypt_profile("A" * 13 ) decrypt_profile(base[:32 ] + admin_block)
6.Byte-at-a-time ECB decryption (Harder) 题目地址:https://www.cryptopals.com/sets/2/challenges/14
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import base64import randomfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesrandom.seed(20260515 ) def randbytes (n ): return bytes ([random.getrandbits(8 ) for _ in range (n)]) def pkcs7_pad (data, block_size=16 ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len def aes_ecb_encrypt (data, key ): cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) encryptor = cipher.encryptor() return encryptor.update(data) + encryptor.finalize() def block_size (oracle ): n = len (oracle(b"" )) for i in range (1 , 64 ): m = len (oracle(b"A" * i)) if m > n: return m - n unknown = base64.b64decode( b"Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg" b"aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq" b"dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg" b"YnkK" ) key = randbytes(16 ) prefix = randbytes(random.randint(5 , 32 )) def oracle (data ): return aes_ecb_encrypt(pkcs7_pad(prefix + data + unknown), key) bs = block_size(oracle) pos = None for i in range (bs * 2 ): x = oracle(b"A" * i + b"B" * (bs * 2 )) blocks = [x[j : j + bs] for j in range (0 , len (x), bs)] for j in range (len (blocks) - 1 ): if blocks[j] == blocks[j + 1 ]: pos = (i, j) break if pos is not None : break pad_len, block_id = pos pad = b"A" * pad_len ans = b"" for _ in range (len (oracle(b"" ))): short = bs - 1 - len (ans) % bs now_block = block_id + len (ans) // bs target = oracle(pad + b"A" * short)[now_block * bs : (now_block + 1 ) * bs] d = {} for i in range (256 ): x = oracle(pad + b"A" * short + ans + bytes ([i]))[now_block * bs : (now_block + 1 ) * bs] d[x] = i if target not in d: break ans += bytes ([d[target]]) print (block_id * bs - pad_len)print (ans.decode().rstrip("\x01" ))
7.PKCS#7 padding validation 题目地址:https://www.cryptopals.com/sets/2/challenges/15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def pkcs7_unpad (data ): pad = data[-1 ] if pad < 1 or pad > 16 or data[-pad:] != bytes ([pad]) * pad: raise ValueError return data[:-pad] print (pkcs7_unpad(b"ICE ICE BABY\x04\x04\x04\x04" ))try : print (pkcs7_unpad(b"ICE ICE BABY\x05\x05\x05\x05" )) except ValueError: print ("ValueError" ) try : print (pkcs7_unpad(b"ICE ICE BABY\x01\x02\x03\x04" )) except ValueError: print ("ValueError" )
8.CBC bit flipping attacks 题目地址:https://www.cryptopals.com/sets/2/challenges/16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import randomimport sysfrom cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modesrandom.seed(20260515 ) def randbytes (n ): return bytes ([random.getrandbits(8 ) for _ in range (n)]) def pkcs7_pad (data, block_size=16 ): pad_len = block_size - len (data) % block_size return data + bytes ([pad_len]) * pad_len def pkcs7_unpad (data ): return data[: -data[-1 ]] def aes_cbc_encrypt (data, key, iv ): cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) encryptor = cipher.encryptor() return encryptor.update(pkcs7_pad(data)) + encryptor.finalize() def aes_cbc_decrypt (data, key, iv ): cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() return decryptor.update(data) + decryptor.finalize() key = randbytes(16 ) iv = randbytes(16 ) prefix = b"comment1=cooking%20MCs;userdata=" suffix = b";comment2=%20like%20a%20pound%20of%20bacon" def encrypt (user_data ): user_data = user_data.replace(b";" , b"%3B" ).replace(b"=" , b"%3D" ) return aes_cbc_encrypt(prefix + user_data + suffix, key, iv) def decrypt (data ): return pkcs7_unpad(aes_cbc_decrypt(data, key, iv)) pad_len = (16 - len (prefix) % 16 ) % 16 payload = b"A" * (pad_len + 16 ) c = bytearray (encrypt(payload)) block_id = (len (prefix) + pad_len) // 16 target = b";admin=true;AAAA" for i in range (len (target)): c[(block_id - 1 ) * 16 + i] ^= 0x41 ^ target[i] plain = decrypt(bytes (c)) if hasattr (sys.stdout, "reconfigure" ): sys.stdout.reconfigure(encoding="utf-8" ) print (b";admin=true;" in plain)print (plain.decode("latin1" ))