Skip to content

feat(crypto): zero-downtime key rotation — RotatingKeyManager (Step 6.7d) - #158

Merged
officialCodeWork merged 1 commit into
mainfrom
build/phase-6/step-6.7d-key-rotation
Jun 8, 2026
Merged

feat(crypto): zero-downtime key rotation — RotatingKeyManager (Step 6.7d)#158
officialCodeWork merged 1 commit into
mainfrom
build/phase-6/step-6.7d-key-rotation

Conversation

@officialCodeWork

Copy link
Copy Markdown
Owner

Summary

Closes Step 6.7 — BYOK with zero-downtime KEK rotation.

  • RotatingKeyManager (rag-core, a crypto-free KeyManager decorator): encrypt always uses the current key; decrypt tries the current key then each non-expired retired key (RetiredKey(key_manager, expires_at)). The try-all decrypt is safe, not a guess — AES-GCM authenticates the DEK, so a wrong KEK can't yield a valid key (only the KEK that wrapped a blob decrypts it).
  • rewrap(ctx, ct) is the background-migration primitive (decrypt with whatever key still works → re-encrypt under the current key); once all blobs are migrated a retired key is dropped.
  • An expired retired key is skipped, so old un-rewrapped data is sealed (EncryptionError) — "retain decrypt-only until expiry" — with no impact on current-key data. Injectable clock for deterministic expiry tests.
  • Composes with every provider (local + all four cloud KMS) since it orchestrates only the KeyManager SPI. ragctl kms --rotate demos the full flow.

Scope / deferred: the rotation mechanism (satisfies the planning rotation acceptance — rotate → old + new both decrypt; expired key seals old data). Config-driven per-tenant multi-generation rotation + the storage-side background re-encryption job land with the EncryptingStorage ingest wiring (tiered storage).

This completes Step 6.7 (6.7a library + 6.7b config/factory/AWS + 6.7c GCP/Azure/Vault + 6.7d rotation); Phase 6 → 7/10.

Tests & gates

  • ~11 new tests (tests/kms/test_rotation.py): rotate → old+new both decrypt; rewrap migration; expired-key sealing; tamper; no-key sealing; multi-generation; clock-controlled expiry; health. Plus ragctl kms --rotate.
  • Full tests/ packages/ 2372 passed, 1 skipped; gateway 290 (unchanged). Green: ruff, mypy --strict (310 files), RAG001, policy-coverage, log-schema; no dist drift (rag-core-only, no config/types/endpoints).

Documentation

Test plan

  • uv run pytest tests/ packages/ -m "not integration" — 2372 passed, 1 skipped
  • uv run pytest apps/gateway/tests — 290 passed
  • ruff + mypy --strict (310) + RAG001 + policy-coverage + log-schema; no dist drift
  • Rotate → old + new decrypt; rewrap migrates; expired retired key seals old data; tamper fails

🤖 Generated with Claude Code

….7d)

Close Step 6.7. RotatingKeyManager (rag-core, a crypto-free KeyManager decorator)
makes rotating a tenant's KEK seamless: encrypt always uses the current key;
decrypt tries the current key then each non-expired retired key
(RetiredKey(key_manager, expires_at)). The try-all decrypt is safe — AES-GCM
authenticates the DEK, so a wrong KEK can't yield a valid key (only the KEK that
wrapped a blob decrypts it).

rewrap(ctx, ct) is the background-migration primitive (decrypt with whatever key
still works → re-encrypt under the current key); once all blobs are migrated a
retired key is dropped. An expired retired key is skipped, so old un-rewrapped
data is sealed (EncryptionError) with no impact on current-key data. Injectable
clock for deterministic expiry tests.

Composes with every provider (local + all four cloud KMS) since it orchestrates
only the KeyManager SPI. ragctl kms --rotate demos the full flow. Scope: the
rotation mechanism; config-driven per-tenant rotation + the storage re-encryption
job land with the EncryptingStorage ingest wiring (tiered storage).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@officialCodeWork
officialCodeWork merged commit 5357741 into main Jun 8, 2026
12 of 17 checks passed
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.

1 participant