Introduce page-level ChaCha20-Poly1305 helpers to enable fused implementations - #259
Conversation
… cipher: chacha20_poly1305_page_encrypt(...) chacha20_poly1305_page_decrypt(...) Initially, these functions would use the existing scalar chacha20_xor() and poly1305() implementations. This is primarily an internal refactoring with no database-format or external API change. The abstraction would allow architecture-specific implementations-particularly a fused WebAssembly SIMD implementation without duplicating or restructuring cipher_chacha20.c.
|
In principle, I have nothing against this restructuring. However, I have some questions.
I will check the PR in more detail after the weekend. |
Yes. I have implemented and benchmarked a fused ChaCha20-Poly1305 path using WebAssembly SIMD. The reported results come from that downstream prototype. On the same hardware, openssl speed with AVX disabled (set OPENSSL_ia32cap=:0) achieves approximately 1.8 GB/s. This suggests that the current fused WASM SIMD implementation, at roughly 1.6 GB/s depending on the runtime and page size, is already reasonably close to the practical performance ceiling for 128-bit SIMD.
No. The fused implementation does not use an additional page-sized buffer. It feeds each ciphertext group into the Poly1305 calculation before overwriting that group with plaintext, interleaving the Poly1305 arithmetic with the ChaCha20 rounds. The final tag comparison can naturally only occur after the entire page has been processed. If the comparison fails, the complete page buffer is securely cleared with I considered this an acceptable compromise because the pager cannot consume the page successfully before the codec callback returns. However, it does not preserve the stronger invariant that no decryption may occur before verification. The scalar implementation in this PR still verifies the tag before decrypting. |
|
Thanks for the detailed information. For the database itself the approach with zeroing the buffer, before returning should work as expected. Although I don't know for sure that If the database is in WAL journal mode things are a bit different. That is, I have to verify that your approach would work there, too. Using an extra local buffer would guarantee to be on the safe side, but it comes at the price of extra memory and of copying the buffer. |
|
I benchmarked our fused WASM SIMD implementation against the prebuilt libsodium.js WASM distribution ( Both implementations perform IETF ChaCha20-Poly1305 encryption and authentication, or decryption and tag verification, using identical inputs. Ciphertext and tags were checked for equality. Median throughput on an AMD Ryzen 7 7840HS, in decimal GB/s, for 64 KiB-sized records (65,504-byte payloads):
Measurements exclude allocations, input/output copies, random-number generation, and I/O. Our implementation uses a benchmark-only adapter to match libsodium’s IETF framing, rather than SQLite3MC’s page format. Even before fusion, our WASM SIMD implementation achieved approximately 1 GB/s for combined encryption and authentication in earlier 4 KiB-page benchmarks. Consequently, the entire advantage over libsodium cannot be attributed to fusion: these results compare different implementations and builds, not a controlled fused-versus-unfused experiment. |
|
I checked the WAL journal mode, and AFAICT it should not impose any additional problem. So, I'm going to merge the PR. Nevertheless, we should discuss whether to use an extra local buffer for decrypting with a fused implementation. A local buffer we would have fully under control, while that is not true for SQLite's page buffer, although the current SQLite implementation behaves correctly. |
|
The implementations of combined chacha20-poly1305 with hardware acceleration for example in OpenSSL are mostly based on assembler code. For SQLite3MC I'd like to avoid separate or embedded assembler code. libsodium's variant for poly1305 can't be compiled with MSVC, because MSVC lacks support for I'd really like to see your fused implementation, so that I can check how much effort it would be to adjust it to architectures x86_64 and aarch64. |
Description
Introduce two internal page-level functions for the ChaCha20-Poly1305 cipher:
chacha20_poly1305_page_encrypt(...)
chacha20_poly1305_page_decrypt(...)
Initially, these functions would use the existing scalar chacha20_xor() and poly1305() implementations. This is primarily an internal refactoring with no database-format or external API change.
The abstraction would allow architecture-specific implementations-particularly a fused WebAssembly SIMD implementation without duplicating or restructuring cipher_chacha20.c.
Type of Change
Please select the type of change:
Motivation
For ordinary authenticated database pages, the current implementation performs ChaCha20 and Poly1305 as separate operations.
This scalar organization is straightforward, but it prevents optimized implementations from combining the two operations.
ChaCha20 and Poly1305 contain largely independent arithmetic. A fused implementation can interleave Poly1305 work with otherwise latency-bound ChaCha20 rounds, reducing separate memory traversal and improving instruction-level parallelism.
This is particularly useful for WebAssembly SIMD, where ChaCha20 can process four blocks in parallel while scalar Poly1305 work is scheduled between the vectorized ChaCha20 rounds.
Benchmarks
For complete authenticated page decryption:
Each sample decrypted 128 MiB.
Current raw fused throughput
The resulting combined ChaCha20-Poly1305 implementation - including encryption and authentication or decryption and tag verification measured:
Checklist