Home  >  Article  >  Backend Development  >  Use cipher.AEAD.Seal() to view memory usage

Use cipher.AEAD.Seal() to view memory usage

WBOY
WBOYforward
2024-02-06 10:03:03964browse

使用 cipher.AEAD.Seal() 查看内存使用情况

Question content

I am using Go's ChaCha20-Poly1305 implementation to encrypt data, but when I encrypt some large files, the memory usage is higher than My expectations. As far as I know, Go's AEAD cipher implementation means we have to keep the entire data in memory to create the hash, but the memory usage is twice the plaintext size.

The following small program that attempts to encrypt 4 GiB of data highlights this (in a real-world program, key and nonce should not be empty):

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)
}

According to the source code of crypto/cipher/gcm.go (used by both AES-GCM and ChaCha20-Poly1305), there are the following comments:

// 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

This means I should be able to re-use the memory, I've tried doing this but it has no effect on the amount of memory used by my application - after calling Seal() we always end up using Can 8 GiB of memory encrypt 4 GiB of data?

[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

If it reuses memory (as implied), then I shouldn't expect any significant increase other than the relatively small hash the AEAD cipher adds to the ciphertext?


Correct answer


You forgot to take into account the authentication token appended to the ciphertext. If you make room for it in the initial allocation, no further allocation is required:

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

The above is the detailed content of Use cipher.AEAD.Seal() to view memory usage. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete