首页 > 解决方案 > 从 AES-CTR 和 AES-GCM 生成的不同密文

问题描述

为了验证我对 AES-GCM 和 AES-CTR 模式的理解,我正在使用 python Crpyto.Cipher 库创建一个简单的示例。我期望两种模式都使用 CTR 方法生成相同的密文。

由于我的目的只是比较来自加密引擎的加密结果,因此,我将 GCM 和 CTR 的消息设置为全 0(十六进制格式)。任何带有 0 的 XOR 都将保留为原始密文。

在 AES-CTR 方面,我将 nonce 设置为“00”。这意味着不会使用随机数,默认情况下,计数器编号将从值 0 开始。

在 AES-GCM 方面,我将 nonce (IV) 设置为 16 字节“00”。我假设这相当于计数器的 0 起始值。

看下面的 AES-GCM 框图,我应该从 AES-GCM 得到的第一个密文应该只是计数器值 1 的加密结果。

但是,我无法从 AES-CTR 和 AES-GCM 获得相同的加密结果。请赐教我在哪个部分犯了错误?最后,我对两种加密模式都使用了相同的 256-AES 密钥。

在此处输入图像描述

这是代码:

key  = bytes.fromhex('0123456789ABCDEF11113333555577770123456789ABCDEF1111333355557777')
msg  = bytes.fromhex('00000000000000000000000000000000')
msg1 = bytes.fromhex('00000000000000000000000000000001')

###### AES-256 ECB Mode ######
aes1 = AES.new(key,AES.MODE_ECB)
print("AES-ECB Result, Counter 1: "+str(binascii.hexlify(aes1.encrypt(msg1)))+"\n")

###### AES-256 CTR Mode ######
aes1 = AES.new(key,AES.MODE_CTR,nonce=bytes.fromhex('00'))
print("AES-CTR Result, Counter 0: "+str(binascii.hexlify(aes1.encrypt(msg))))
print("AES-CTR Result, Counter 1: "+str(binascii.hexlify(aes1.encrypt(msg)))+"\n")

###### AES-256 GCM Mode ######
aes1 = AES.new(key, AES.MODE_GCM, nonce=bytes.fromhex('00000000000000000000000000000000'))
ciphertext, authTag = aes1.encrypt_and_digest(msg)
print("AES-GCM Result, Counter 0: "+str(binascii.hexlify(ciphertext)))
print("AES-GCM Initialization Vector: "+str(binascii.hexlify(aes1.nonce)))

蟒蛇结果:

AES-ECB Result, Counter 1: b'24c82c75b5546a77d20c9868503767b4'

AES-CTR Result, Counter 0: b'4a85984511e5ca3f03297d84c69584c4'
AES-CTR Result, Counter 1: b'24c82c75b5546a77d20c9868503767b4'

AES-GCM Result: b'dfff0d463d8254d7eb23887729b22a85'
AES-GCM Initialization Vector: b'00000000000000000000000000000000'

标签: python-3.xcryptographyaesaes-gcm

解决方案


问题是维基百科对 GCM 的工作方式有点误导。“计数器 0”不像 CTR 中那样是一堆零。它是“预计数器”块 (J0)。如NIST 800-38d中所述:

在步骤 2 中,预计数器块 (J0) 从 IV 生成。特别地,当IV的长度为96位时,则将填充字符串0³¹||1附加到IV以形成预计数器块。否则,IV 将填充最少数量的“0”位,可能没有,因此结果字符串的长度是 128 位(块大小)的倍数;该字符串依次附加了 64 个额外的“0”位,然后是 IV 长度的 64 位表示,并且 GHASH 函数应用于生成的字符串以形成预计数器块。

在您的示例中,您传递了一个 128 位块,因此:

  • 不添加任何填充(它已经是 128 位)
  • 附加 8 个字节的 0
  • 附加IV(16)的长度:0x0000000000000010

然后通过 GHASH 函数运行所有这些。这为您提供了 J0,在图形上称为“计数器 0”。

获取计数器 1:

在步骤 3 中,将 32 位递增函数应用于预计数器块以生成初始计数器块,用于在明文上调用 GCTR 函数。

32 位递增函数的基本意思是“递增最右边的 32 位”。

这就是输入 AES 的内容。这将是非常随机的,当然不会像您的 CTR 案例中那样为“1”。这样做允许任意长度的随机数。

但还有另一种特殊情况:GCM 的 96 位随机数,这是推荐的配置。在这种情况下,J0 要简单得多。它只是附加了 32 位 01 的随机数。这是通过增量函数传递的,这意味着“第一个”块将是 02。

如果您为 GCM 使用 96 位随机数,并将其与 CTR 的第三块进行比较,它们将匹配。

aes1 = AES.new(key,AES.MODE_CTR,nonce=bytes.fromhex('000000000000000000000000000000'))
print("AES-CTR Result, Counter 0: "+str(binascii.hexlify(aes1.encrypt(msg))))
print("AES-CTR Result, Counter 1: "+str(binascii.hexlify(aes1.encrypt(msg))))
print("AES-CTR Result, Counter 2: "+str(binascii.hexlify(aes1.encrypt(msg)))+"\n")

aes1 = AES.new(key, AES.MODE_GCM, nonce=bytes.fromhex('000000000000000000000000'))
print("AES-GCM Result, Counter 0: "+str(binascii.hexlify(aes1.encrypt(msg))))

==>

AES-CTR Result, Counter 0: b'4a85984511e5ca3f03297d84c69584c4'
AES-CTR Result, Counter 1: b'24c82c75b5546a77d20c9868503767b4'
AES-CTR Result, Counter 2: b'c8f656193e3bb5b6117d49e3c6799864'  <===

AES-GCM Result, Counter 0: b'c8f656193e3bb5b6117d49e3c6799864'  <===

推荐阅读