Implement external ownership for AES-GCM context - #8178
Implement external ownership for AES-GCM context#8178Eddy Ashton (eddyashton) wants to merge 4 commits into
Conversation
Co-authored-by: eddyashton <6000239+eddyashton@users.noreply.github.com>
…m-contexts' into agents/context-object-ownership-implementation
There was a problem hiding this comment.
Pull request overview
This PR extends CCF’s AES-GCM symmetric key abstraction to support externally owned, reusable “pre-keyed” cipher contexts (KeyAesGcm::Context via make_context()), and wires this into ledger encryption by giving each LedgerSecret a reusable context protected by a mutex. It also adds new concurrency/behavior tests and a microbenchmark to validate and measure the new approach.
Changes:
- Introduce
ccf::crypto::KeyAesGcm::Contextandmake_context()for reusable, non-concurrently-used AES-GCM contexts. - Update the OpenSSL AES-GCM implementation to create and reuse pre-keyed
EVP_CIPHER_CTXinstances. - Update ledger encryption to use
LedgerSecret-owned reusable contexts, plus add concurrent tests and an AES-GCM benchmark.
Custom instructions used:
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/node/test/encryptor.cpp | Adds a concurrent encrypt/decrypt test and strengthens rollback lifetime checks. |
| src/node/ledger_secret.h | Adds per-LedgerSecret reusable AES-GCM context with mutex-guarded encrypt/decrypt helpers. |
| src/kv/encryptor.h | Switches to retrieving LedgerSecret and calling its encrypt/decrypt wrappers. |
| src/crypto/test/crypto.cpp | Adds AES-GCM context-reuse, empty-input, and concurrency tests. |
| src/crypto/test/bench.cpp | Adds AES-GCM encryption microbench using a reusable context. |
| src/crypto/openssl/symmetric_key.h | Adds make_context() to the OpenSSL-backed AES-GCM key and moves cleansing to out-of-line dtor. |
| src/crypto/openssl/symmetric_key.cpp | Refactors AES-GCM encrypt/decrypt around reusable pre-keyed contexts. |
| include/ccf/crypto/symmetric_key.h | Extends public API with KeyAesGcm::Context and make_context(). |
| CHANGELOG.md | Documents the new public API for reusable AES-GCM contexts. |
Suppressed comments (4)
src/crypto/openssl/symmetric_key.cpp:152
- On authentication failure, decrypt_with_context() returns false without clearing the output buffer. Callers that reuse the plain vector can accidentally observe stale plaintext from a previous successful decrypt.
if (EVP_DecryptFinal_ex(ctx, nullptr, &final_outl) != 1)
{
return false;
}
src/crypto/openssl/symmetric_key.cpp:165
- decrypt_with_context() only assigns to the output plain when cipher is non-empty. For valid AAD-only messages (empty cipher), the function returns true but leaves plain unchanged, so callers reusing the vector may see stale data.
if (!cipher.empty())
{
plain = std::move(plaintext);
}
src/crypto/openssl/symmetric_key.cpp:157
- Typo in comment: should refer to GCM (not GSM) and fix grammar (“As long as …”).
// As long a we use GSM cipher, the final outl must be 0, because there's
// no padding and the block size is equal to 1, so EncryptUpdate() always
// does the whole thing. Final is still a must to finalize and check the
// error.
src/crypto/test/crypto.cpp:921
- Similarly, this empty-cipher decrypt assertion seeds decrypted as empty, so it won’t catch a bug where decrypt() returns true but leaves stale data in the output buffer. Seed the output with non-empty data first.
decrypted.clear();
REQUIRE(empty_aes_gcm_key->decrypt(iv, empty_tag, {}, {}, decrypted));
REQUIRE(decrypted.empty());
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This pull request implements an alternative approach to the context object ownership as discussed in #8170. The key changes include:
KeyAesGcm::Contextthat can be created throughmake_context(), allowing for faster encryption and decryption calls.KeyAesGcm::encrypt/decryptcalls.LedgerSecretnow owns a reusable context, synchronized with a mutex to ensure thread safety.EVP_aes_*_gcm()calls for improved performance.Validation
This implementation enhances the performance and usability of the AES-GCM encryption while ensuring thread safety and compatibility with existing APIs.