为什么带有 GCM 的 AES-256 在密文大小上增加了 16 个字节?

Dan*_*ip3 2 encryption aes go nonce aes-gcm

我正在使用 Golang 的加密包,特别是 crypto/aes,带有 32 字节的密钥(因此,AES-256)和 GCM 方法(Galois/Counter Mode)。
我从一个文件中读取了多个 16384 字节的块并生成了一个密码块、一个 GCM 方法和一个 12 字节的随机数。
然后,我将随机数添加到密文中,以便在解密时将它们拆分,以访问随机数(因为 12 字节的大小是已知的)。

人们会期望生成的密文是 16384 + 12 个字节 = 16396;但是,在实际加密时,我得到的大小为 16412 字节,因此添加了 16 字节。解密每个块后,我得到 16384 的“正常”大小。

这是一个快速示例。

block, _ := aes.NewCipher([]byte("W9FLKnyv397R82kKuFpfp6y8usGRf49a"))
gcm, _ := cipher.NewGCM(block)
nonce = make([]byte, gcm.nonceSize()) // nonceSize is 12 bytes
_, _ = io.ReadFull(rand.Reader, nonce) // populate nonce with random data


for {
    src := make([]byte, 1024 * 16) // let's hypotise this src is a chunk of a file, full of 1024 * 16 bytes, so 16384

    encryptedBytes := gcm.Seal(nonce, nonce, src, nil) // this prepends the nonce to the src, thus adding 12 bytes in front of the encrypted string

    /*
    Now, encryptedBytes should be 16384 + 12 bytes long, but it is 16384 + 12 + 16.
    If I want to decrypt a chunk of the encrypted bytes, I need to use the size of 16384 + 12 + 16 and this makes it unpractical.
    */
}
Run Code Online (Sandbox Code Playgroud)

这似乎不是因为填充(也因为 GCM 不使用填充)。

那么,为什么 AES 会在我的密文中添加 16 个字节?

kel*_*aka 7

AES-GCM 提供机密性、完整性和身份验证。要提供最后两个,需要一个身份验证标签。

始终计算 16 字节标记大小,并在您的情况下附加它。


更多细节;

  1. 密文大小:这总是等于明文大小,因为 AES-GCM 内部使用 CTR 模式进行加密,不需要填充。

  2. Nonce/IV 大小: GCM 可以接受大的 nonce 大小(或小),但是,建议使用 12 字节,因为它不需要额外的过程。12 字节以外的任何值都使用 GHASH 处理;

     if len(IV) = 96 then 
         J_0 = IV || 0^{31}1
     else 
         J_0=GHASH_H(IV||0^{s+64}||len(IV_64))
    
    Run Code Online (Sandbox Code Playgroud)

    Nonce 通常被添加到消息中,这是密文大小的增加。

  3. 标签大小: GCM 总是输出 16 字节的标签大小。可以修剪它,但是,它会降低防伪的安全性。

    标签通常附加在密文中。

    遵守NIST 特别出版物 800-38D(第 8 页)

    标记的位长,用 t 表示,是一个安全参数,如附录 B 所述。一般来说,t 可以是以下五个值中的任何一个:128、120、112、104 或 96。对于某些应用程序, t 可能是 64 或 32;附录 C 中给出了使用这两种标签长度的指南,包括在这些情况下对输入数据长度和密钥寿命的要求。

因此,您可能会看到类似的输出 (Nonce|ciphertext|tag)