Go编程,cipher.AEAD.Seal(),内存优化,ChaCha20-Poly1305,Golang加密算法
在使用Go语言中的ChaCha20-Poly1305实现进行数据加密时,有时会发现内存使用量比预期要高。这是因为Go语言的AEAD(Authenticated Encryption with Associated Data)密码实现的方式要求我们将整个数据保存在内存中以创建哈希值,而不仅仅是明文大小。
示例
下面是一个简单的例子程序,加密4 GiB的数据:
package main import ( "fmt" "os" "runtime" "golang.org/x/crypto/chacha20poly1305" ) func main() { showMemUsage("START") plaintext := make([]byte, 4*1024*1024*1024) // 4 GiB showMemUsage("STAGE 1") key := make([]byte, chacha20poly1305.KeySize) if cipher, err := chacha20poly1305.New(key); err == nil { showMemUsage("STAGE 2") nonce := make([]byte, chacha20poly1305.NonceSize) cipher.Seal(plaintext[:0], nonce, plaintext, nil) } showMemUsage("END") } func showMemUsage(tag string) { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Fprintf(os.Stdout, "[%s] Alloc = %v MiB, TotalAlloc = %v MiB\n", tag, m.Alloc/1024/1024, m.TotalAlloc/1024/1024) }
根据源代码注释,我们可以使用`plaintext[:0]`作为目标切片来重用明文的存储空间。然而,在这个例子中,无论我们如何尝试重用内存,程序总是使用了8 GiB的内存来加密4 GiB的数据。
[START] Alloc = 0 MiB, TotalAlloc = 0 MiB [STAGE 1] Alloc = 4096 MiB, TotalAlloc = 4096 MiB [STAGE 2] Alloc = 4096 MiB, TotalAlloc = 4096 MiB [END] Alloc = 8192 MiB, TotalAlloc = 8192 MiB
为什么会出现这种情况?
ChaCha20-Poly1305密码的工作原理
ChaCha20-Poly1305是一个AEAD密码,它将明文与随机生成的nonce结合起来,并使用密钥进行加密。同时,它还会生成一个16字节的认证标签(authentication tag),附加到密文的末尾。在解密时,我们需要验证认证标签以确保密文的完整性和真实性。
解析代码
根据源代码注释和问题中提到的话题,我们可以看出问题可能出在切片的容量上。根据Go语言的规范,切片的长度可以小于或等于其容量,但不能超过容量。
首先创建了一个大小为4 GiB的明文切片`plaintext`。然后,我们生成了一个密钥`key`并使用它初始化了一个ChaCha20-Poly1305密码实例`cipher`。
我们生成了一个随机的nonce,并调用`cipher.Seal()`方法来对明文进行加密。
问题中提到的疑惑在于,即使我们尝试重用在cipher.AEAD.Seal()方法中,参数dst被用于存储加密后的数据,而参数plaintext作为输入明文数据。根据源代码注释,如果我们希望重用明文的存储空间用于加密输出,我们应该使用`plaintext[:0]`作为dst参数。这样做将确保加密后的数据直接写入到原始明文切片中,并避免额外的内存分配。
在我们的示例程序中,无论我们如何尝试重用`plaintext[:0]`作为dst参数,程序都会占用8 GiB的内存来加密4 GiB的数据。这是因为Go语言的cipher.AEAD.Seal()方法在进行加密操作时,会为密文数据分配一个新的切片并返回。虽然我们使用了`plaintext[:0]`作为dst参数,但实际上它不会改变分配的内存量。
那么有没有办法减少内存占用呢?
答案是:有的。我们可以通过手动创建一个长度已知且足够容纳密文的切片,然后将其传递给cipher.AEAD.Seal()方法。这样做可以避免cipher.AEAD.Seal()方法中的额外内存分配。
下面是修改后的示例代码:
package main import ( "fmt" "os" "runtime" "golang.org/x/crypto/chacha20poly1305" ) func main() { showMemUsage("START") plaintext := make([]byte, 4*1024*1024*1024) // 4 GiB showMemUsage("STAGE 1") key := make([]byte, chacha20poly1305.KeySize) if cipher, err := chacha20poly1305.New(key); err == nil { showMemUsage("STAGE 2") nonce := make([]byte, chacha20poly1305.NonceSize) ciphertext := make([]byte, len(plaintext)+cipher.Overhead()) cipher.Seal(ciphertext[:0], nonce, plaintext, nil) } showMemUsage("END") } func showMemUsage(tag string) { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Fprintf(os.Stdout, "[%s] Alloc = %v MiB, TotalAlloc = %v MiB\n", tag, m.Alloc/1024/1024, m.TotalAlloc/1024/1024) }
在修改后的代码中,我们首先创建了一个足够容纳密文的切片`ciphertext`,并且长度已知为明文切片`plaintext`的长度加上密码算法的Overhead(即认证标签的大小)。然后,我们将`ciphertext[:0]`作为dst参数传递给cipher.AEAD.Seal()方法,以确保加密后的数据直接写入到原始切片中。
通过这种方式,我们可以减少内存占用并提高性能。运行修改后的示例程序,你会发现内存使用量与明文大小相匹配:
[START] Alloc = 0 MiB, TotalAlloc = 0 MiB [STAGE 1] Alloc = 4096 MiB, TotalAlloc = 4096 MiB [STAGE 2] Alloc = 4096 MiB, TotalAlloc = 8192 MiB [END] Alloc = 8192 MiB, TotalAlloc = 8192 MiB
现在,程序只使用了与明文大小相匹配的内存(4 GiB),而不再占用8 GiB的内存。这是因为我们手动创建了一个足够容纳密文的切片,并将其传递给cipher.AEAD.Seal()方法,避免了额外的内存分配。文章来源:https://www.toymoban.com/diary/golang/662.html
通过优化cipher.AEAD.Seal()方法的内存使用,我们可以提高加密性能并减少内存消耗,从而更好地满足应用程序的需求。文章来源地址https://www.toymoban.com/diary/golang/662.html
到此这篇关于如何高效优化Go中cipher.AEAD.Seal()的内存使用的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!