AES-CTR & AES-GCM 生成的密文不同
Different generated Ciphertext from AES-CTR & AES-GCM
为了验证我对 AES-GCM 和 AES-CTR 模式的理解,我正在使用 python Crpyto.Cipher 库创建一个简单示例。我期望从两种模式生成相同的密文,这两种模式都使用 CTR 方法。
由于我的目的只是比较来自加密引擎的加密结果,因此,我将 GCM 和 CTR 的消息都设置为全 0(十六进制格式)。任何与 0 的 XOR 都将保留为原始密文。
在 AES-CTR 方面,我将随机数设置为“00”。这意味着不会使用随机数,默认情况下计数器编号将从值 0 开始。
在 AES-GCM 方面,我将随机数 (IV) 设置为 16 字节“00”。我假设这相当于计数器的 0 起始值。
看下面的AES-GCM框图,我应该从AES-GCM得到的第一个密文应该只是计数器值1的加密结果。
但是,我无法从 AES-CTR & AES-GCM 获得相同的加密结果。请赐教我哪一部分出错了?最后,我对两种加密模式使用相同的 256-AES-key。
代码如下:
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)))
Python 结果:
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'
问题是维基百科对 GCM 的工作原理有点误导。 “计数器 0”不像 CTR 中那样是一串零。它是“pre-counter”块 (J0)。如NIST 800-38d所述:
In Step 2, the pre-counter block (J0) is generated from the IV. In particular, when the length of the IV is 96 bits, then the padding string 0³¹||1 is appended to the IV to form the pre-counter block. Otherwise, the IV is padded with the minimum number of ‘0’ bits, possibly none, so that the length of the resulting string is a multiple of 128 bits (the block size); this string in turn is appended with 64 additional ‘0’ bits, followed by the 64-bit representation of the length of the IV, and the GHASH function is applied to the resulting string to form the pre- counter block.
在您的示例中,您传递了一个 128 位块,因此:
- 不附加填充(它已经是 128 位)
- 追加 8 个字节的 0
- 追加 IV (16) 的长度:0x0000000000000010
然后 运行 所有这些都通过 GHASH 函数。这给你 J0,在图形上称为“计数器 0”。
获取计数器 1:
In Step 3, the 32-bit incrementing function is applied to the pre-counter block to produce the initial counter block for an invocation of the GCTR function on the plaintext.
32 位递增函数基本上意味着“递增 right-most 32 位。”
这就是输入 AES 的内容。这将是非常随机的,当然不像您的点击率案例中的“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' <===
为了验证我对 AES-GCM 和 AES-CTR 模式的理解,我正在使用 python Crpyto.Cipher 库创建一个简单示例。我期望从两种模式生成相同的密文,这两种模式都使用 CTR 方法。
由于我的目的只是比较来自加密引擎的加密结果,因此,我将 GCM 和 CTR 的消息都设置为全 0(十六进制格式)。任何与 0 的 XOR 都将保留为原始密文。
在 AES-CTR 方面,我将随机数设置为“00”。这意味着不会使用随机数,默认情况下计数器编号将从值 0 开始。
在 AES-GCM 方面,我将随机数 (IV) 设置为 16 字节“00”。我假设这相当于计数器的 0 起始值。
看下面的AES-GCM框图,我应该从AES-GCM得到的第一个密文应该只是计数器值1的加密结果。
但是,我无法从 AES-CTR & AES-GCM 获得相同的加密结果。请赐教我哪一部分出错了?最后,我对两种加密模式使用相同的 256-AES-key。
代码如下:
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)))
Python 结果:
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'
问题是维基百科对 GCM 的工作原理有点误导。 “计数器 0”不像 CTR 中那样是一串零。它是“pre-counter”块 (J0)。如NIST 800-38d所述:
In Step 2, the pre-counter block (J0) is generated from the IV. In particular, when the length of the IV is 96 bits, then the padding string 0³¹||1 is appended to the IV to form the pre-counter block. Otherwise, the IV is padded with the minimum number of ‘0’ bits, possibly none, so that the length of the resulting string is a multiple of 128 bits (the block size); this string in turn is appended with 64 additional ‘0’ bits, followed by the 64-bit representation of the length of the IV, and the GHASH function is applied to the resulting string to form the pre- counter block.
在您的示例中,您传递了一个 128 位块,因此:
- 不附加填充(它已经是 128 位)
- 追加 8 个字节的 0
- 追加 IV (16) 的长度:0x0000000000000010
然后 运行 所有这些都通过 GHASH 函数。这给你 J0,在图形上称为“计数器 0”。
获取计数器 1:
In Step 3, the 32-bit incrementing function is applied to the pre-counter block to produce the initial counter block for an invocation of the GCTR function on the plaintext.
32 位递增函数基本上意味着“递增 right-most 32 位。”
这就是输入 AES 的内容。这将是非常随机的,当然不像您的点击率案例中的“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' <===