HMAC 未能检测到键上的细微变化
HMAC failing to detect slight change on keys
假设用于加密文件的密钥是这个。
SDEREvalYDHK3xcuzChG7CU4hLBaoaVSvaJg_Fqo7UY=
加密工作正常。但是,当我使用 hmac.compare_digest() 时,未检测到所用密钥的细微变化。
SDEREvalYDHK3xcuzChG7CU4hLBaoaVSvaJg_Fqo7UZ=
请注意倒数第二个字符已从 Y
更改为 Z
。解密仍然有效,但我预计它会失败。
我做错了什么?如果有任何帮助,我正在使用 PyCryptodome 模块。
import os, hmac, hashlib
from base64 import urlsafe_b64encode, urlsafe_b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
BS = AES.block_size
class CRYPTOPRACTICE:
def key(self):
with open("k.txt", "rb") as k:
return urlsafe_b64decode(k.read())
def e(self, file):
with open(file, "rb") as data:
IV = os.urandom(BS)
e = AES.new(self.key(), AES.MODE_CBC, IV).encrypt(pad(data.read(), BS))
sig = hmac.new(self.key(), e, hashlib.sha256).digest()
with open(file + ".encrypted", "wb") as enc:
enc.write(IV + e + sig)
os.remove(file)
def d(self, file):
with open(file, "rb") as data:
IV = data.read(BS)
e = data.read()[:-32]
data.seek(-32, os.SEEK_END)
sig = data.read()
auth = hmac.new(self.key(), e, hashlib.sha256).digest()
if hmac.compare_digest(sig, auth):
d = unpad(AES.new(self.key(), AES.MODE_CBC, IV).decrypt(e), BS)
with open(file[:-10], "wb") as dec:
dec.write(d)
data.close()
os.remove(file)
else: print(f"Fail: {file}")
a = CRYPTOPRACTICE()
a.e("test.txt")
a.d("test.txt.encrypted")
您假设微小的变化确实改变了二进制文件。但是,base 64 将 6 位编码为一个字符。这也意味着,除非您对 3 个字节的倍数进行编码,否则填充字符 之前的 最后一个字符可能无法编码完整的 6 位。
在你的例子中有一个最后的填充字符,这意味着最后三个字符编码 2 * 8 = 16 位,而它们可以编码 3 * 6 = 18 位。所以最后两位(索引到 base 64 字母表中的)通常设置为零,否则将被忽略。通常解码器也简单地忽略编码的两位。所以除非你做出更大的改变,否则字符编码完全相同的 4 位。
如果你有两个填充字符,那么你甚至有 4 位设置为零。如果没有填充字符,那么每个字符都必须相同,否则二进制文件将改变。
假设用于加密文件的密钥是这个。
SDEREvalYDHK3xcuzChG7CU4hLBaoaVSvaJg_Fqo7UY=
加密工作正常。但是,当我使用 hmac.compare_digest() 时,未检测到所用密钥的细微变化。
SDEREvalYDHK3xcuzChG7CU4hLBaoaVSvaJg_Fqo7UZ=
请注意倒数第二个字符已从 Y
更改为 Z
。解密仍然有效,但我预计它会失败。
我做错了什么?如果有任何帮助,我正在使用 PyCryptodome 模块。
import os, hmac, hashlib
from base64 import urlsafe_b64encode, urlsafe_b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
BS = AES.block_size
class CRYPTOPRACTICE:
def key(self):
with open("k.txt", "rb") as k:
return urlsafe_b64decode(k.read())
def e(self, file):
with open(file, "rb") as data:
IV = os.urandom(BS)
e = AES.new(self.key(), AES.MODE_CBC, IV).encrypt(pad(data.read(), BS))
sig = hmac.new(self.key(), e, hashlib.sha256).digest()
with open(file + ".encrypted", "wb") as enc:
enc.write(IV + e + sig)
os.remove(file)
def d(self, file):
with open(file, "rb") as data:
IV = data.read(BS)
e = data.read()[:-32]
data.seek(-32, os.SEEK_END)
sig = data.read()
auth = hmac.new(self.key(), e, hashlib.sha256).digest()
if hmac.compare_digest(sig, auth):
d = unpad(AES.new(self.key(), AES.MODE_CBC, IV).decrypt(e), BS)
with open(file[:-10], "wb") as dec:
dec.write(d)
data.close()
os.remove(file)
else: print(f"Fail: {file}")
a = CRYPTOPRACTICE()
a.e("test.txt")
a.d("test.txt.encrypted")
您假设微小的变化确实改变了二进制文件。但是,base 64 将 6 位编码为一个字符。这也意味着,除非您对 3 个字节的倍数进行编码,否则填充字符 之前的 最后一个字符可能无法编码完整的 6 位。
在你的例子中有一个最后的填充字符,这意味着最后三个字符编码 2 * 8 = 16 位,而它们可以编码 3 * 6 = 18 位。所以最后两位(索引到 base 64 字母表中的)通常设置为零,否则将被忽略。通常解码器也简单地忽略编码的两位。所以除非你做出更大的改变,否则字符编码完全相同的 4 位。
如果你有两个填充字符,那么你甚至有 4 位设置为零。如果没有填充字符,那么每个字符都必须相同,否则二进制文件将改变。