现代密码学-Cryptography 实验二
摘要
实验二主要是关于对称密码中的分组密码(块密码),主要就是AES加密中关于ECB、CBC模式的漏洞,padding的漏洞等等。
T1
题目
题目描述的较为冗长,实际上大致就是护照上有一部分信息泄露了,泄露的信息为:
12345678<8<<<1110182<111116?<<<<<<<<<<<<<<<4
通过查阅文献,可以通过校验和以及后续很多操作恢复密钥,然后通过密钥恢复AES加密的密文。
分析
这道题的主要难度就是查阅文献,没有什么特殊技巧,我就简单描述一下解密流程。
- 首先恢复泄露信息中的?,经过查阅题目提供的文献[2],可以得知“?”是前面“111116”根据一定权重得到的校验和,根据文献[2]即可恢复“?”。
- 通过泄露信息中的“12345678”“1110182”“111116?”这三部分(被称为MRZ),计算MRZ的SHA-1哈希值能够得到\(k_{seed}\)。
- 在\(k_{seed}\)后面加上'00000001'比特串得到比特串D,计算D的SHA-1哈希值,哈希值的前16位为\(k_{a}\),16-32位为\(k_{b}\)。
- 对3得到的\(k_{a}\)或\(k_{b}\),将其第8*i位改成这位前7个数字的偶校验码,得到最终的\(k_{a}\)和\(k_{b}\)。
- 将4得到的\(k_{a}\)和\(k_{b}\)相接,即得到最终用于AES加密的秘钥Key。
- 利用秘钥Key解密得到明文。
完整代码如下:
点击查看代码
from hashlib import sha1
from base64 import b64decode
from Crypto.Cipher import AES
#参数
C = '9MgYwmuPrjiecPMx61O6zIuy3MtIXQQ0E59T3xB6u0Gyf1gYs2i3K9Jxaa0zj4gTMazJuApwd6+jdyeI5iGHvhQyDHGVlAuYTgJrbFDrfB22Fpil2NfNnWFBTXyf7SDI'
K = '12345678<8<<<1110182<111116?<<<<<<<<<<<<<<<4'
#根据文献[2]求?
def solve_num(k):
k = list(k)
weights = [7, 3, 1, 7, 3, 1]
sum = 0
for i in range(21, 27):
sum = (sum + int(k[i]) * weights[i - 21]) % 10
k[27] = str(sum)
return ''.join(k)
#求K_seed
def getK_seed(k):
mrz_imt = k[:10] + k[13:20] + k[21:28]
H_SHA1 = sha1(mrz_imt.encode()).hexdigest()
return H_SHA1[:32]
#增加偶校验码以得到ka和kb
def getKab(k):
kab = []
a = bin(int(k,16))[2:]
for i in range(0, len(a), 8):
kab.append(a[i:i + 7])
if a[i:i + 7].count('1') % 2 == 0:
kab.append('1')
else:
kab.append('0')
return hex(int(''.join(kab), 2))[2:]
#根据ka和kb求Key
def getKey(k):
k = k + '00000001'
H = sha1(bytes.fromhex(k)).hexdigest()
return getKab(H[:16]) + getKab(H[16:32])
#求明文
def getP(C, k):
C = b64decode(C)
aes = AES.new(bytes.fromhex(k), AES.MODE_CBC, bytes.fromhex('0'*32))
return aes.decrypt(C).decode()
if __name__ == '__main__':
K = solve_num(K)
K_seed = getK_seed(K)
Key = getKey(K_seed)
P = getP(C, Key)
print(P)
T2
题目
题目链接the cryptopals crypto challenges set 2
一共有八道题,对每道题单独分析。
T2-1
题目
A block cipher transforms a fixed-sized block (usually 8 or 16 bytes) of plaintext into ciphertext. But we almost never want to transform a single block; we encrypt irregularly-sized messages.
One way we account for irregularly-sized messages is by padding, creating a plaintext that is an even multiple of the blocksize. The most popular padding scheme is called PKCS#7.
思路
单纯地实现PKCS#7的填充方式。由于该情况下没有传输错误等情况,因此我就只写了填充和去填充的代码,没有写检验填充是否正确的代码。
完整代码如下:
点击查看代码
# 添加padding
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
message_pading = pad(b'YELLOW SUBMARINE', 16)
print(message_pading)
print(unpad(message_pading))
T2-2
题目
CBC mode is a block cipher mode that allows us to encrypt irregularly-sized messages, despite the fact that a block cipher natively only transforms individual blocks.
In CBC mode, each ciphertext block is added to the next plaintext block before the next call to the cipher core.
The first plaintext block, which has no associated previous ciphertext block, is added to a "fake 0th ciphertext block" called the initialization vector, or IV.
Implement CBC mode by hand by taking the ECB function you wrote earlier, making it encrypt instead of decrypt (verify this by decrypting whatever you encrypt to test), and using your XOR function from the previous exercise to combine them.
The file here is intelligible (somewhat) when CBC decrypted against "YELLOW SUBMARINE" with an IV of all ASCII 0 (\x00\x00\x00 &c)
这里就是让我们实现块密码的CBC模式,利用给出的初始向量IV和密钥k,对文件进行解密。
思路
题目提示我们可以使用这套题前面的ECB加密和异或代码,不过这套题前面两道没有写啊,所以咱自己实现就好了,至于ECB直接使用库中包装好的函数就好了,然后按CBC模式设计即可。
完整代码如下:
点击查看代码
from Crypto.Cipher import AES
from base64 import b64decode
# 添加padding
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
# AES ECB模式加密
def AES_ECB_encrypt(plaintext: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(pad(plaintext, AES.block_size))
#AES ECB模式解密
def AES_ECB_decrypt(ciphertext: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
return cipher.decrypt(ciphertext)
#异或
def xor(a: bytes, b: bytes) -> bytes:
return bytes(x ^ y for x, y in zip(a, b))
#AES CBC模式加密
def AES_CBC_encrypt(plaintext: bytes, key: bytes, iv: bytes) -> bytes:
cipher=b''
prev=iv
plaintext=pad(plaintext, AES.block_size)
for i in range(0, len(plaintext), AES.block_size):
current_plaintext_block = plaintext[i:i + AES.block_size]
block_cipher_input=xor(current_plaintext_block, prev)
block_cipher_output=AES_ECB_encrypt(block_cipher_input, key)
cipher+=block_cipher_output
prev=block_cipher_output
return cipher
#AES CBC模式解密
def AES_CBC_decrypt(ciphertext: bytes, key: bytes, iv: bytes) -> bytes:
plaintext=b''
prev=iv
for i in range(0, len(ciphertext), AES.block_size):
current_ciphertext_block = ciphertext[i:i + AES.block_size]
block_plaintext_input=AES_ECB_decrypt(current_ciphertext_block, key)
block_plaintext_output=xor(block_plaintext_input, prev)
plaintext+=block_plaintext_output
prev=current_ciphertext_block
return plaintext
iv=b'\x00'* AES.block_size
key=b'YELLOW SUBMARINE'
with open('10.txt') as plaintext_file:
plaintext=b64decode(plaintext_file.read())
print(AES_CBC_decrypt(plaintext, key, iv).decode().rstrip())
(ps:解密出来疑似是一首歌的歌词)
T2-3
题目
Now that you have ECB and CBC working:
Write a function to generate a random AES key; that's just 16 random bytes.
Write a function that encrypts data under an unknown key --- that is, a function that generates a random key and encrypts under it.
The function should look like:
encryption_oracle(your-input)
=> [MEANINGLESS JIBBER JABBER]
Under the hood, have the function append 5-10 bytes (count chosen randomly) before the plaintext and 5-10 bytes after the plaintext.
Now, have the function choose to encrypt under ECB 1/2 the time, and under CBC the other half (just use random IVs each time for CBC). Use rand(2) to decide which to use.
Detect the block cipher mode the function is using each time. You should end up with a piece of code that, pointed at a block box that might be encrypting ECB or CBC, tells you which one is happening.
题目意思大概是写一个函数根据随机密钥进行加密,在加密前在明文的前后各添加5到10字节随机值,随机选取加密方式为ECB或CBC,如果是CBC,还需要选择随机的IV。根据加密的信息,判断是使用ECB加密还是CBC加密。
思路
经过学习我们知道ECB加密对于同一条明文加密会得到同一个结果,即使添加了随机前后缀,其大体模样也不会差太多,而CBC加密结果随IV变化很大。因此根据这一点,我们选择连续三个块内容一致的明文,根据是否有密文块重复,判断其是否为ECB加密。
完整代码如下:
点击查看代码
import os
import random
import Crypto.Cipher.AES as AES
#随机密钥
def random_key():
return os.urandom(16)
#随机前后缀
def random_padding():
return os.urandom(random.randint(5, 10))
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
#选择加密机
def encryption_oracle(key, message):
mode = random.choice([AES.MODE_ECB, AES.MODE_CBC])
plaintext = random_padding() + message + random_padding()
plaintext = pad(plaintext, 16)
match mode:
case AES.MODE_ECB:
return AES.new(key, mode).encrypt(plaintext), mode
case AES.MODE_CBC:
iv = random_key()
return AES.new(key, mode, iv).encrypt(plaintext), mode
assert False, "unreachable"
#检测预言机
def detect_mode_oracle(ciphertext):
blocks = [ciphertext[i : i + 16] for i in range(0, len(ciphertext), 16)]
if len(blocks) != len(set(blocks)):
return AES.MODE_ECB
return AES.MODE_CBC
key = random_key()
msg = b"\x00" * 16 * 3
encrypted = [encryption_oracle(key, msg) for _ in range(1000)]
accr = sum(detect_mode_oracle(ciphertext) == mode for ciphertext, mode in encrypted)
print(f"{accr / len(encrypted):.2%}")
T2-4
题目
Copy your oracle function to a new function that encrypts buffers under ECB mode using a consistent but unknown key (for instance, assign a single random key, once, to a global variable).
Now take that same function and have it append to the plaintext, BEFORE ENCRYPTING, the following string:
Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK
Base64 decode the string before appending it. Do not base64 decode the string by hand; make your code do it. The point is that you don't know its contents.
What you have now is a function that produces:
AES-128-ECB(your-string || unknown-string, random-key)
It turns out: you can decrypt "unknown-string" with repeated calls to the oracle function!
题目大体意思就是你可以通过控制明文前的前缀破解ECB加密的密文。
思路
题目下面已经给出了解题思路:
- 通过不断改变前缀的长度,试探出块的大小(比较懒所以省略了)。
- 用T2-3中的预言机来检测这是一个ECB加密(这步我永也省略了)。
- 设计一个比块长度少一比特的前缀X,那么第一个块的最后一比特就是明文的第一比特,记录此时第一块的密文。
- 改变前缀为X+Y,Y是1比特的字符,遍历Y,直到第一块密文与已知密文一致。
- 那么,Y即为明文第一个字母。
- 针对下一字母继续破解,直到破译全部明文。
完整代码如下:
点击查看代码
import base64
import os
import Crypto.Cipher.AES as AES
import string
# 添加padding
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
#加密函数
def AES_ECB_encrpt(control_text:bytes):
key = os.urandom(16)
plaintext = pad(control_text + base64.b64decode("""
Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK"""), 16)
return AES.new(key, AES.MODE_ECB).encrypt(plaintext)
#枚举得到明文长度
init_unk_strlen = len(AES_ECB_encrpt(b""))
unk_strlen = init_unk_strlen
for i in range(16):
if len(AES_ECB_encrpt(b"A" * i)) != init_unk_strlen:
unk_strlen = init_unk_strlen - i
break
#DFS得到明文
plain_space = string.printable.encode()
def dfs(known_text: bytes):
while True:
partial = known_text[-15:]
partial = b"\x00" * (15 - len(partial)) + partial
current = []
for i in plain_space:
oracle = partial + bytes([i]) + b"\x00" * (15 - len(known_text) % 16)
enc = AES_ECB_encrpt(oracle)
if enc[15] == enc[len(known_text) // 16 * 16 + 31]:
current.append(i)
if len(current) == 1:
known_text += bytes(current)
if len(known_text) == unk_strlen: # 达到预期长度,成功退出
print(known_text.decode())
return True
continue
elif len(current) == 0:
return False
else:
for c in current:
if dfs(known_text + bytes([c])):
return True
dfs(b'')
T2-5
题目
Now write a function that encodes a user profile in that format, given an email address. You should have something like
profile_for("foo@bar.com")
... and it should produce:
{
email: 'foo@bar.com',
uid: 10,
role: 'user'
}
... encoded as:
email=foo@bar.com&uid=10&role=user
Your "profile_for" function should not allow encoding metacharacters (& and =). Eat them, quote them, whatever you want to do, but don't let people set their email address to "foo@bar.com&role=admin".
Now, two more easy functions. Generate a random AES key, then:
- Encrypt the encoded user profile under the key; "provide" that to the "attacker".
- Decrypt the encoded user profile and parse it.
Using only the user input to profile_for() (as an oracle to generate "valid" ciphertexts) and the ciphertexts themselves, make a role=admin profile.
题目大意就是给你一个用户名和基于该用户信息加密后的密文,修改密文使其能解密为合法的明文,且该明文中的用户名变成role。
思路
- 构造一个邮箱地址,这个邮箱的加密的第一个明文块为“email=xxxxxxxxxx”,第二个明文块为“admin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b”,这样能得到第二个明文块对应的密文块,即以admin结尾的密文块,我构造了如下的地址:
xxxxxxxxxxadmin\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b
- 构造一个邮箱地址,要求是profile_for()后最后一块的明文块只包含“user”,具体来说这道题需要一个13比特的邮箱地址,然后得到其密文,我构造了如下的地址:
master@xd.com
- 取2中得到前两个密文块,再取1中得到的第二密文块,拼接起来。这样构造的密文解密就能将user改成admin。
完整代码如下:
点击查看代码
from Crypto.Cipher import AES
from Crypto import Random
# 添加padding
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
# AES ECB模式加密
def AES_ECB_encrypt(plaintext: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(pad(plaintext, AES.block_size))
#AES ECB模式解密
def AES_ECB_decrypt(ciphertext: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_ECB)
return cipher.decrypt(ciphertext)
#profile
def profile_for(email):
email=email.replace('&','').replace('=','')
return {'email':email, 'uid':10, 'role':'user'}
#字典转字符串
def kv_encode(dict_object):
encode_text=''
for item in dict_object.items():
encode_text += item[0] + '=' + str(item[1]) + '&'
return encode_text[:-1]
#字符串转字典
def kv_decode(encode_text):
dict_object={}
attributes=encode_text.split('&')
for item in attributes:
dict_object[item.split('=')[0]]=item.split('=')[1]
return dict_object
#ECB模式加解密
class ECBoracle:
def __init__(self):
self.key=Random.new().read(AES.key_size[0])
def encrypt(self, email):
encoded=kv_encode(profile_for(email))
bytes_to_encrypted=encoded.encode()
return AES_ECB_encrypt(bytes_to_encrypted, self.key)
def decrypt(self, ciphertext):
return unpad(AES_ECB_decrypt(ciphertext, self.key))
#cut and paste攻击
def cut_and_paste_attack(oracle):
prefix_len = AES.block_size - len('email=')
suffix_len = AES.block_size - len('admin')
email1 = 'x' * prefix_len + 'admin' + (chr(suffix_len)*suffix_len)
encrypt1 = oracle.encrypt(email1)
email2 = "master@xd.com"
encrypt2 = oracle.encrypt(email2)
ciphertext = encrypt2[:32] + encrypt1[16:32]
return ciphertext
oracle=ECBoracle()
ciphertext=cut_and_paste_attack(oracle)
decrypt = oracle.decrypt(ciphertext).decode()
plaintext = kv_decode(decrypt)
print(plaintext)
T2-6
题目
Take your oracle function from T2-4. Now generate a random count of random bytes and prepend this string to every plaintext. You are now doing:
AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)
Same goal: decrypt the target-bytes.
题目是大体与T2-4一致,不过在加密时会添加一个固定的随机产生的前缀。
思路
这里我们认为这个随机前缀产生后就不会改变,那么除了求明文长度外,还得求前缀导致的偏移量和需要的补齐长度,这里我们补齐长度确定为填满后再填两个块,只需修改深度优先搜索中的部分代码即可。
完整代码如下:
点击查看代码
import base64
import os
import Crypto.Cipher.AES as AES
import string
import random
# 先生成随机长度,随机生成的前缀
prefix_len = random.randint(0, 64)
prefix = os.urandom(prefix_len)
# 添加padding
def pad(message:bytes, block_size:int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
#去除padding
def unpad(message:bytes) -> bytes:
padding = message[-1]
return message[:-padding]
#加密函数
def AES_ECB_encrpt(control_text:bytes):
key = os.urandom(16)
plaintext = pad(prefix + control_text + base64.b64decode("""
Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK"""), 16)
return AES.new(key, AES.MODE_ECB).encrypt(plaintext)
#枚举得到明文长度,前缀导致的偏移量和需要的补齐长度
def get_unklen():
init_unk_strlen = len(AES_ECB_encrpt(b""))
unk_strlen = init_unk_strlen
for i in range(16):
if len(AES_ECB_encrpt(b"A" * i)) != init_unk_strlen:
unk_strlen = init_unk_strlen - i
break
leftlen = 0
while True:
leftlen += 1
enc = AES_ECB_encrpt(b"A" * leftlen)
blocks = [enc[i : i + 16] for i in range(0, len(enc), 16)]
for i in range(len(blocks) - 1):
if blocks[i] == blocks[i + 1]:
return unk_strlen - i * 16 + leftlen % 16, i * 16, leftlen % 16
unk_strlen, offset, leftpad = get_unklen()
leftpad = b"\x00" * leftpad
#DFS得到明文
plain_space = string.printable.encode()
def dfs(known_text):
while True:
partial = known_text[-15:]
partial = b"\x00" * (15 - len(partial)) + partial
current = []
for i in plain_space:
oracle = leftpad + partial + bytes([i]) + b"\x00" * (15 - len(known_text) % 16)
enc = AES_ECB_encrpt(oracle)[offset:]
if enc[15] == enc[len(known_text) // 16 * 16 + 31]:
current.append(i)
if len(current) == 1:
known_text += bytes(current)
if len(known_text) == unk_strlen:
print(known_text.decode())
return True
continue
elif len(current) == 0:
return False
else:
for c in current:
if dfs(known_text + bytes([c])):
return True
dfs(b"")
T2-7
题目
Write a function that takes a plaintext, determines if it has valid PKCS#7 padding, and strips the padding off.
就是写一个验证padding是否合法的程序。
思路
检查padding最后一个字符,得到字符对应数字,看前面是否有相应数量的字符。
完整代码如下:
点击查看代码
def pad(message: bytes, block_size: int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
def unpad(message_padded):
padding_len = message_padded[-1]
message, padding = message_padded[:-padding_len], message_padded[-padding_len:]
assert all(x == padding_len for x in padding)
return message
print(unpad(b"ICE ICE BABY\x04\x04\x04\x04"))
print(unpad(b"ICE ICE BABY\x05\x05\x05\x05"))
T2-8
题目
Generate a random AES key.
Combine your padding code and CBC code to write two functions.
The first function should take an arbitrary input string, prepend the string:
"comment1=cooking%20MCs;userdata="
.. and append the string:
";comment2=%20like%20a%20pound%20of%20bacon"
The function should quote out the ";" and "=" characters.
The function should then pad out the input to the 16-byte AES block length and encrypt it under the random AES key.
The second function should decrypt the string and look for the characters ";admin=true;" (or, equivalently, decrypt, split the string on ";", convert each resulting string into 2-tuples, and look for the "admin" tuple).
Return true or false based on whether the string exists.
If you've written the first function properly, it should not be possible to provide user input to it that will generate the string the second function is looking for. We'll have to break the crypto to do that.
Instead, modify the ciphertext (without knowledge of the AES key) to accomplish this.
You're relying on the fact that in CBC mode, a 1-bit error in a ciphertext block:
Completely scrambles the block the error occurs in
Produces the identical 1-bit error(/edit) in the next ciphertext block.
题目大意就是在CBC加密模式下。可以通过修改IV和密文块实现对明文块的控制。
思路
为了便于操作我就假设明文为":admin<true",这样需要翻转的位数少一点,根据明文的结构,我们只需要将‘:’改为‘;’,‘<’改为‘=’,即可骗过检测程序。不过,还需要保证明文在同一个加密块中。由于固定前缀长30比特,我们添加2比特使其成为完整块,那么明文":admin<true"一定在第三个块中,然后对比特进行翻转即可。
完整代码如下:
点击查看代码
import Crypto.Cipher.AES as AES
import os
key = os.urandom(16)
# 填充
def pad(message: bytes, block_size: int) -> bytes:
padding = block_size - len(message) % block_size
return message + bytes([padding] * padding)
# 去除填充
def unpad(message_padded):
padding_len = message_padded[-1]
message, padding = message_padded[:-padding_len], message_padded[-padding_len:]
assert all(x == padding_len for x in padding)
return message
# CBC模式加密
def AES_CBC_encrypt(userdata: bytes):
data = (
b"comment1=cooking MCs;userdata="
+ userdata.replace(b";", b"%3B").replace(b"=", b"%3D")
+ b";comment2= like a pound of bacon"
)
return AES.new(key, AES.MODE_CBC, os.urandom(16)).encrypt(pad((b"\x00" * 16) + data, 16))
# CBC模式解密
def AES_CBC_decrypt(data: bytes):
data = unpad(AES.new(key, AES.MODE_CBC, os.urandom(16)).decrypt(data))[16:]
return {
(kv := item.split(b"=", maxsplit=1))[0].decode(): kv[1]
for item in data.split(b";")
}
# 检测函数
def is_admin(data: bytes):
decrypted = AES_CBC_decrypt(data)
return decrypted.get("admin") == b"true"
padlen = 2
userdata = b"A" * padlen + b":admin<true"
enc = bytearray(AES_CBC_encrypt(userdata))
enc[padlen + 30] ^= ord(":") ^ ord(";")
enc[padlen + 36] ^= ord("<") ^ ord("=")
if is_admin(enc):
print("Success!")
else:
print("Fail!")
总结
这次实验主要涉及块密码相关知识,需要对AES,以及ECB、CBC加密模式比较熟悉。在这里感谢同班好兄弟鲤唐可可,我借鉴了其部分代码与思路鲤唐可可的代码。
我的所有代码存储在Github上Cryptography Assignment

浙公网安备 33010602011771号