Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce memory usage when AEAD en/decrypting large messages #259

Merged
merged 6 commits into from
Dec 16, 2024

Conversation

twiss
Copy link
Member

@twiss twiss commented Dec 10, 2024

The AEAD code buffered the data to be en/decrypted at multiple points. Firstly, when decrypting, the ciphertext was buffered in order to not pre-allocate the chunk size. However, given RFC9580 caps the chunk size at 4MiB, and most (all?) OSes reuse empty pages, the overhead of allocating the the entire chunk size (256KiB by default in our implementation) is not that large. This leads to a large reduction in memory usage when decrypting large messages, at the cost of somewhat increased memory usage when decrypting small messages. For an added reduction, the ciphertext buffer can be reused for the plaintext, and also for the buffered plaintext when it's read across multiple calls to Read().

Similarly, we can reduce the memory usage when encrypting large messages (though not by as much) by manually buffering the plaintext to be encrypted carefully to reserve space for the tag. That way, the plaintext buffer can be reused for the ciphertext.

Finally, fix OCB en/decryption when reusing the buffer.

Before (chunk size = 256KiB):

BenchmarkEncryptOpenPgpAEAD1KB-20           1000              5906 ns/op           11637 B/op         69 allocs/op
BenchmarkDecryptOpenPgpAEAD1KB-20           1000              7413 ns/op           15664 B/op         82 allocs/op
BenchmarkEncryptOpenPgpAEAD1MB-20           1000           1452205 ns/op         6853508 B/op        104 allocs/op
BenchmarkDecryptOpenPgpAEAD1MB-20           1000           2319484 ns/op        11728449 B/op        337 allocs/op
BenchmarkEncryptOpenPgpAEAD4MB-20           1000           3911609 ns/op        26908267 B/op        202 allocs/op
BenchmarkDecryptOpenPgpAEAD4MB-20           1000           6586654 ns/op        46843797 B/op       1064 allocs/op

After (chunk size = 256KiB):

BenchmarkEncryptOpenPgpAEAD1KB-20           1000             33286 ns/op          279538 B/op         62 allocs/op
BenchmarkDecryptOpenPgpAEAD1KB-20           1000             34539 ns/op          277458 B/op         65 allocs/op
BenchmarkEncryptOpenPgpAEAD1MB-20           1000           1441909 ns/op         5083626 B/op         65 allocs/op
BenchmarkDecryptOpenPgpAEAD1MB-20           1000           1361304 ns/op         5515768 B/op         92 allocs/op
BenchmarkEncryptOpenPgpAEAD4MB-20           1000           3265659 ns/op        17863129 B/op         67 allocs/op
BenchmarkDecryptOpenPgpAEAD4MB-20           1000           3672796 ns/op        21383718 B/op        110 allocs/op

After (chunk size = 4MiB):

BenchmarkEncryptOpenPgpAEAD1KB-20           1000             15371 ns/op           82896 B/op         62 allocs/op
BenchmarkDecryptOpenPgpAEAD1KB-20           1000             18963 ns/op           80848 B/op         65 allocs/op
BenchmarkEncryptOpenPgpAEAD1MB-20           1000           1257834 ns/op         4690397 B/op         67 allocs/op
BenchmarkDecryptOpenPgpAEAD1MB-20           1000           1633398 ns/op         5319211 B/op        104 allocs/op
BenchmarkEncryptOpenPgpAEAD4MB-20           1000           3122793 ns/op        18059736 B/op         69 allocs/op
BenchmarkDecryptOpenPgpAEAD4MB-20           1000           3707209 ns/op        21187301 B/op        159 allocs/op

After (chunk size = message size):

BenchmarkEncryptOpenPgpAEAD1KB-20           1000              5402 ns/op           10320 B/op         62 allocs/op
BenchmarkDecryptOpenPgpAEAD1KB-20           1000              4605 ns/op            8277 B/op         65 allocs/op
BenchmarkEncryptOpenPgpAEAD1MB-20           1000           1416058 ns/op         4690407 B/op         67 allocs/op
BenchmarkDecryptOpenPgpAEAD1MB-20           1000           1530036 ns/op         5319209 B/op        104 allocs/op
BenchmarkEncryptOpenPgpAEAD4MB-20           1000           3060694 ns/op        18059737 B/op         69 allocs/op
BenchmarkDecryptOpenPgpAEAD4MB-20           1000           3710514 ns/op        21187300 B/op        159 allocs/op

Because of the above results, it may be worth setting the chunk size to the message size when known (e.g. in gopenpgp).

Copy link
Contributor

@lubux lubux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done, I went over most of the logic.
The only thing I did not get is the ar.peekedBytes in the first chunk, but I might have missed something.

ocb/ocb.go Show resolved Hide resolved
openpgp/packet/aead_encrypted.go Show resolved Hide resolved
openpgp/packet/aead_crypter.go Outdated Show resolved Hide resolved
openpgp/packet/aead_crypter.go Show resolved Hide resolved
@twiss twiss force-pushed the less-memory-large-msgs branch from 172524b to 1fd5ec8 Compare December 16, 2024 11:50
@twiss twiss merged commit be3aef0 into main Dec 16, 2024
8 checks passed
@twiss twiss deleted the less-memory-large-msgs branch December 16, 2024 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants