1

我正在编写一个非常简单的脚本,它将根据这个公式解密一个文本字节,P′2[K] = Pn[K] ⊕ Cn-1[K] ⊕ C′[K] oracle 是一个简单的函数,它解密然后检查最后一个字节是否等于 padding 0x15。p'2[k] 只是 0x15(填充大小)

def decrypt(cipher):
    dec = aes_context.decryptor()
    text = dec.update(cipher)
    if text[-1] == 0x15:
        return True, "Padding Match"
    else:
        return False, "No Match"

但行为似乎 Undefined 。该循环是一个从 0-> 255 的简单循环(尝试解密一个块的次数)

number = 0x01
index = 0
while index < 255:
    try_this_block = 0x0.to_bytes(7, "big") + number.to_bytes(1, "big")
    mod_ciphertext = try_this_block + c1
    state, error_text = decrypt(mod_ciphertext)
    if state:
        byte = try_this_block[-1] ^ 0x15 ^ c1[-1]
        text_back += byte.to_bytes(1, "big")
        break
    else:
        number += 1
    index += 1

被加密的消息只是 8 字节字符串 + 8 字节填充,并且每次都使用相同的密钥和 IV 进行解密。与c1,c2对应m1,m2的密文

m1 = b"khaled G"
m2 = 0x00.to_bytes(7, "big") + 0x015.to_bytes(1, "big")

完整的源代码在这里:

from cryptography.hazmat.primitives.ciphers import algorithms, modes, Cipher
from cryptography.hazmat.backends import default_backend
import os

m1 = b"khaled G"
m2 = 0x00.to_bytes(7, "big") + 0x015.to_bytes(1, "big")

aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(os.urandom(16)), default_backend())
enc = aes_context.encryptor()

c1 = enc.update(m1)
c2 = enc.update(m2)
c1, c2 = c2[0:8], c2[8:]

def decrypt(cipher):
    dec = aes_context.decryptor()
    text = dec.update(cipher)
    if text[-1] == 0x15:
        return True, "Padding Match"
    else:
        return False, "No Match"

text_back = b""
number = 0x01
index = 0
while index < 255:
    try_this_block = 0x0.to_bytes(7, "big") + number.to_bytes(1, "big")
    mod_ciphertext = try_this_block + c1
    state, error_text = decrypt(mod_ciphertext)
    if state:
        byte = try_this_block[-1] ^ 0x15 ^ c1[-1]
        text_back += byte.to_bytes(1, "big")
        break
    else:
        number += 1
    index += 1
print("text is {}".format(text_back))
4

1 回答 1

1

我认为代码中有两个问题:

  • 首先,AES 用于生成长度为一个块或 16 个字节的密文。然后将这个 16 字节的密文块分成两个 8 字节的块,作为填充预言攻击的 2 个单独的密文块。然而,这在关系的上下文中是不允许的,P2 = C1 xor (C1' xor P2')因为两个块之间的假定关系不存在,参见例如here。在这一点上,我不想排除您的情况有算法的可能性,但它会是一个不同的算法。
    该问题可以通过使用与算法相同的块大小来解决。例如,对于 8 字节的块大小,可以使用 TripleDES。或者,当使用 AES 时,必须使用 16 字节的块大小。在前一种情况下,必须调整密钥和 IV 大小,而且该行c1, c2 = c2[0:8], c2[8:]现在已过时:

    aes_context = Cipher(algorithms.TripleDES(os.urandom(24)), modes.CBC(os.urandom(8)), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    
  • 其次,解密没有考虑IV。如果c1要解密第一个块c1c0要解决此问题,需要进行以下更改:

    iv = os.urandom(8)
    aes_context = Cipher(algorithms.TripleDES(os.urandom(24)), modes.CBC(iv), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

    通过这些更改,获得了预期的结果 ( G)。

当使用 AES 和 16 字节的块大小时,必须另外相应地修改数据。

  • 例如,以下更改也提供了预期的结果 ( G):

    m1 = b"khaled Gkhaled G"
    m2 = 0x00.to_bytes(15, "big") + 0x015.to_bytes(1, "big")
    ...
    iv = os.urandom(16)
    aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(iv), default_backend())
    ...
    c1 = enc.update(m1)
    c2 = enc.update(m2)
    #c1, c2 = c2[0:8], c2[8:]
    ...
    try_this_block = 0x0.to_bytes(15, "big") + number.to_bytes(1, "big")
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

编辑:

  • 如果改变算法/块大小(AES,16 字节)和测试数据,则测试数据将仅形成一个明文块(由m1和组成m2)而不是两个。在这种情况下,0x15作为结果返回,因为填充字节现在是块的最后一个字节。关于必要的代码更改,需要注意的是,当只加密一个明文块时,只生成一个密文块(c1):

    iv = os.urandom(16)                                                                     
    aes_context = Cipher(algorithms.AES(os.urandom(16)), modes.CBC(iv), default_backend())  
    ...
    c1 = enc.update(m1)  # empty
    c1 += enc.update(m2) # provides full block
    #c1, c2 = c2[0:8], c2[8:]
    ...
    try_this_block = 0x0.to_bytes(15, "big") + number.to_bytes(1, "big")
    ...
    byte = try_this_block[-1] ^ 0x15 ^ iv[-1]
    

    这将作为结果返回0x15

于 2020-08-22T18:16:51.840 回答