Skip to content

Keep pre-warmed encryption state between operations#74

Open
bifurcation wants to merge 8 commits intomainfrom
prewarmed-cipher-state
Open

Keep pre-warmed encryption state between operations#74
bifurcation wants to merge 8 commits intomainfrom
prewarmed-cipher-state

Conversation

@bifurcation
Copy link
Contributor

@bifurcation bifurcation commented Mar 5, 2026

Summary

Addresses #55 by caching cipher contexts to avoid redundant key schedule computation on every seal()/open() call.

  • Add CipherState class to crypto.h with RAII-managed cipher handle
  • KeyRecord now holds a std::unique_ptr<CipherState> for cached state
  • All three backends (OpenSSL 3.x, OpenSSL 1.1, BoringSSL) implement caching for both GCM and CTR+HMAC suites

How it works

GCM suites: EVP_CIPHER_CTX is cached and initialized once with the key. On each operation, EVP_EncryptInit_ex(ctx, nullptr, nullptr, nullptr, nonce) resets with the new nonce while preserving the key schedule.

CTR+HMAC suites: Both AES-CTR (EVP_CIPHER_CTX) and HMAC contexts are cached. HMAC_Init_ex(ctx, nullptr, 0, nullptr, nullptr) resets HMAC state while preserving the key.

Fixes #55

Test plan

  • All 8 existing tests pass
  • AES-CTR-HMAC Test Vectors pass (validates CTR+HMAC caching)
  • SFrame Test Vectors pass (validates GCM caching)

🤖 Generated with Claude Code

bifurcation and others added 8 commits March 5, 2026 10:32
Addresses #55 by caching cipher contexts to avoid redundant key schedule
computation on every seal/open call.

Changes:
- Add CipherState class to crypto.h with RAII-managed cipher handle
- KeyRecord now holds a std::unique_ptr<CipherState> for cached state
- All three backends (OpenSSL 3.x, OpenSSL 1.1, BoringSSL) implement:
  - GCM suites: EVP_CIPHER_CTX cached and reset with new nonce only
  - CTR+HMAC suites: Both AES-CTR and HMAC contexts cached

The key optimization is that EVP_EncryptInit_ex/EVP_DecryptInit_ex with
nullptr for cipher preserves the key schedule while updating the nonce.
Similarly, HMAC_Init_ex with nullptr key preserves the HMAC key state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- CipherHandle is now EVP_CIPHER_CTX via reinterpret_cast
- Add separate HmacHandle struct for HMAC state
- CipherState members are now cipher_handle and hmac_handle
- Use unique_ptr throughout to avoid raw pointer temporaries
- Fix BoringSSL endif comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Consolidate CipherDeleter and HmacDeleter into single Deleter struct
  with overloaded operator()
- For OpenSSL 1.1 and BoringSSL, HmacHandle is now a direct cast to
  HMAC_CTX (like CipherHandle to EVP_CIPHER_CTX)
- For OpenSSL 3.x, HmacHandle struct uses unique_ptr for members
- Move scoped typedefs to top of files, remove duplicates
- Use scoped_evp_ctx and scoped_hmac_ctx in create_seal/create_open

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Define CipherHandle and HmacHandle as actual structs wrapping
  unique_ptr members (no more reinterpret_cast)
- Remove forward declarations, move helper functions earlier in file
- Consolidate seal_ctr/open_ctr/seal_aead/open_aead to take contexts
- Stateless seal/open now create temporary CipherState and delegate
- Removes ~840 lines of duplicate code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The struct HMAC name conflicts with OpenSSL's HMAC function.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

Keep pre-warmed encryption state

1 participant