Carries the resolved key in the write partition - #125
Merged
Merged
Conversation
The engine's encryption cache id never hashes the key, so a write after a mint found the warm entry from before it and wrapped its data key under the old version. The write side's partition id (an encrypt and a rekey's write half) now also carries the resolved key's identity, in a length-prefixed pre-image; the read side keeps decision 7's formula. ADR-0001 Amendment B (proposed) records the formula, why the pre-image cannot collide and the one-time cold cache on deploy; a Note on ADR-0005 says what the P2 step 1 drain still covers. Ruled by the operator, 2026-09-30. Refs: enc-jwkn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The engine's encryption cache id hashes the partition id, the suite and the context, never the key that wrapped the entry's data key. With the partition id built from the vault and the selector only (ADR-0001 decision 7), a write after a key version was minted found the warm entry from before the mint and wrapped its data key under the old version until the entry expired or the cache was dropped.
This change folds the resolved key into the write side's partition id. An encrypt and a rekey's write half now derive the id from the vault, the selector and the key
encryption_key/2resolved to (the namespace and name of anEncryptor.Key.Aes, the key id of anEncryptor.Key.Kms). The pre-image is length-prefixed, and the digest and 16-byte truncation are unchanged. A new version therefore gets a cold partition, and the write after a mint wraps under it at once, with no drain. The read side keeps decision 7's formula. Ruled by the operator, 2026-09-30.Encryptor.Vault.Partition.encryption_id/3(@doc false) computes the write-side id;Partition.id/2is unchanged.Encryptor.Vault.Encrypt.stack/4andclient/4build the write-side stack;stack/3andclient/3remain the read side's (Encryptor.Vault.Decrypt, a rekey's read half).Encryptor.Vault.Rekey's write half callsclient/4.Encryptor.Vault.Encrypt'spartition_id/2and the tests.Records
changelog.d/enc-jwkn.md(Fixed) names the one-time cold cache on upgrade.Tests
shred_drain_test.exs, "P2 step 1, the mint, on a warm encryption cache". The existing test is flipped: it proves the cache is warm (the second write before the mint reuses the first's encrypted data key), then asserts that the write after the mint names version 2 with no drain. The new test "a rekey's write half rewrites under the new version at once, with no drain" warms the rekey's own write partition with two rewrites before the mint, then asserts that the rewrite after it names version 2.partition_test.exs: two versions give distinct ids; a moved field boundary gives a distinct id (length prefix); AES and KMS keys are tagged apart; the vault and the selector still partition; the derivation is pinned. The two pinned hex values were computed by an independent implementation outside Elixir.encrypt_test.exs:stack/4's partition id isencryption_id/3and differs across versions and fromstack/3's.Gate
mix qualitygreen on the committed tree (684 of 684 tests, Credo, Dialyzer, Docs, doc links).Review tier
This is a contract-tier PR: it changes what the write side's caching CMM keys on (a behaviour ADR-0001 decision 7 on main cites), and it carries a record Amendment.
Provenance
Encryptor.Vault.Partition, as a@doc falsefunction, so the module stays the only place a partition id is computed without adding a public function. The read and write stacks are two arities of the internalstack/clientrather than one tagged argument, soEncryptor.Vault.Decryptneeds no edit.Refs: enc-jwkn