我正在使用GO的ChaCha20-Poly1305实现来加密数据,但是由于我正在加密一些大文件,所以内存使用量比我预期的要高.我知道Go的AEAD密码实现意味着我们必须将整个数据保存在内存中,以便它创建散列,但内存使用量是明文的两倍.

以下试图加密4 GiB数据的小程序突出显示了这一点(在实际程序中,keynonce不应为空):

package main

import (
  "os"
  "fmt"
  "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)
}

根据crypto/cipher/gcm.go的源代码(由AES-GCM和ChaCha20-Poly1305使用),有以下注释:

// To reuse plaintext's storage for the encrypted output, use plaintext[:0]
// as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.
Seal(dst, nonce, plaintext, additionalData []byte) []byte

这意味着我应该能够重用内存,我已经try 过这样做,但这对我的应用程序使用的内存量没有任何影响-在调用Seal()之后,我们总是使用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

如果它是在重复使用内存(如所暗示的),那么我不应该指望会有任何大规模的增长,除了AEAD密码向密文添加了相对较小的哈希?

推荐答案

您忘记了说明附加到密文中的身份验证标记.如果您在初始分配中为其腾出空间,则不需要进一步分配:

package main

import (
        "fmt"
        "os"
        "runtime"

        "golang.org/x/crypto/chacha20poly1305"
)

func main() {
        showMemUsage("START")

        plaintext := make([]byte, 4<<30, 4<<30+chacha20poly1305.Overhead)

        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>>20, m.TotalAlloc>>20)
}

// Output:
// [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 = 4096 MiB, TotalAlloc = 4096 MiB

开销是Poly1305身份验证标记的大小,以及密文长度与其明文之间的差值.

Go相关问答推荐

如何用Golang解码这个嵌套的json?

如何配置vscode以在Go中显示不必要的(过度指定的)泛型?

Go:拆分一个由逗号分隔的键/值对字符串,并在给定的键/价值对中嵌入可能的逗号

Golang在不写入磁盘的情况下为jpeg图像生成一致的哈希

在 Go 中使用 Apache Arrow 按时间间隔对事件进行分区

如何在 fyne-io/fyne 中设置文本的字母间距?

hyperledger fabric - go:在 $PATH 中找不到可执行文件

在 Cloud Run 中找不到默认凭据

Go 切片容量增长率

如何在 Golang http.Request 对象中读取自定义 ajaxParams

从Go中的随机日期开始以天为单位获取时间

使用 os/exec 和在命令行执行之间的结果莫名其妙地不同

如何在 helm 中将字符串连接到 .AsConfig 的结果?

如何将多个切片打印为一个切片?

如何在 golang 中同时加载 .env 文件和 os 环境变量

panic :拨号 tcp:在 172.22.64.1:53 上查找 bookstoreDB:没有这样的主机

如何在gorm中处理多个查询

Go:如何创建一个可以提供配置文件中描述的 url 的服务器

如何在 Go 中使用 Pact 返回错误请求(400、500)?

Go generics:我会在哪里使用 any 而不是 interface{}?