A warehouse rotates its column-encryption key every year - why do seven-year-old encrypted rows still decrypt afterwards?
answer
- a write-side change
- no stored row was read
- the marker travels with the bytes
- the decrypt routes itself
- retirement is a separate act
basics
~20 sRotation changes only which key version encrypts new writes. Each stored ciphertext records the version that produced it, and the manager still holds the earlier versions, so old rows decrypt under the key that actually made them.
solid answer
~40 sA versioned key is a numbered series of independent key materials published under one logical name, not a single value with a history. Rotating it mints version `n+1` and moves the pointer that says which version encrypts new writes; it rewrites nothing already stored. Every ciphertext carries a marker naming the version that produced it, so a read hands the bytes to the manager, the manager selects that version's material, and a row written seven versions ago decrypts without anyone remembering which key was current in 2019. Earlier versions therefore stay usable until someone explicitly retires them - a separate, deliberate act, and the one that actually makes old rows unreadable. Rotation on its own bounds what a future exposure of the current material would reach; it touches no existing row.
code
json · 7 lines{
"rowId": "2019-04-11/000817",
"writtenAt": "2019-04-11T02:14:55Z",
"keyName": "warehouse-columns",
"keyVersion": 2,
"ciphertext": "<opaque bytes>"
}go deeper
Recall the shape: one key name, many numbered versions, and each encrypted value remembers the version number that made it. Rotating mints a new version for future writes and leaves everything already stored exactly as it is.
Explain the mechanics: the version marker stored beside the ciphertext, the manager resolving that version's material on decrypt, and the pointer that only decides what new writes use. Be able to say what a rotation costs, and why it is constant rather than proportional to the data.
Show the operational consequence: rotation is not re-protection, live versions accumulate, and each one must be kept, backed up and restorable. Know that retirement is the separate act that changes readability, and that it fails closed across backups and archives too.
Frame the tradeoff you are buying: cheap forward-looking rotation in exchange for a growing set of live key versions the estate must custody forever. Decide deliberately how many versions is acceptable and what funds the backfill that reduces them.
## What a versioned key actually is A **versioned key** is not one value with an edit history. It is a numbered series of independent key materials published under a single logical name: version 1, version 2 and version 7 are different keys that happen to share a label. Two pieces of state sit beside the series: - **the current version** - the one the manager uses whenever a caller asks it to encrypt something new; - **the set of versions still usable for decryption** - normally every version ever minted, minus any an operator has explicitly retired. A rotation moves the first pointer and mints one new key. It does not read stored data, does not schedule a rewrite, and does not disable anything. That is why rotating the key behind seven years of encrypted columns finishes in the time it takes to generate a key, and it is the property that surprises anyone who pictures rotation as *everything gets re-protected*. ## Why last year's rows still read Because **the ciphertext records which version produced it**. The version number travels with the encrypted bytes - a header inside the blob, a prefix, or a plain column beside it - and it is not secret, so carrying it costs nothing. A decrypt is then self-routing: 1. the reader hands the stored ciphertext to the manager; 2. the manager reads the version marker; 3. it selects that version's key material; 4. it decrypts and returns the plaintext. Nobody on that path has to remember which key was in force when the row was written. A query spanning seven years of partitions can touch seven different key versions in one scan without the query knowing any of them exist. Strip the marker out and only bad options remain: trial-decrypt against every live version until one succeeds, or maintain an external map from rows to versions that must stay correct for the whole retention period, survive restores, and never be lost. Both turn retiring a version into guesswork, which is why the marker belongs to the stored format rather than being treated as an optimisation. ## What the rotation changed, and what it did not | Rotation changed | Rotation did not change | |---|---| | which version encrypts new writes | any ciphertext already stored | | the number of live versions, by one | which version an existing row names | | what a later exposure of the current material reaches | what an earlier version can still decrypt | | the key that new partitions will be written under | the readability of last year's partitions | The asymmetry is the whole design. Rotation is cheap precisely because it is forward-looking: it bounds what the *next* exposure of the current key material would reach, and it deliberately leaves the corpus alone. Re-protecting the corpus is a separate, expensive job - a **backfill** - and choosing not to run one is a legitimate decision rather than an oversight, provided everybody knows the older versions are still live. ## Retirement is a separate, deliberate act The state that actually makes old rows unreadable is retirement: disabling a version, or declaring a **minimum accepted version** below which the manager refuses to decrypt. Designs genuinely differ here - some managers let you name a floor and refuse anything beneath it, others only let you enable and disable individual versions, and some also allow the material to be destroyed outright. What they share is that it is an operator action separate from rotation, and that it **fails closed**: every stored ciphertext still naming a retired version becomes undecryptable, including copies sitting in backups, read replicas and archives taken while that version was current. That is why the order matters. You rotate, new writes accumulate under the new version, you decide whether to backfill, and only then do you retire - never the reverse. ## Where engineers get this wrong - **"The rotation re-encrypted the data."** It did not read a single row. - **"The old version is gone."** It is live until retired, and usually nothing retires it automatically. - **"Last year's rows are protected by the new key now."** They are protected by the key that encrypted them, which has not changed. - **"We rotate yearly, so only one key is live."** After seven yearly rotations with seven-year retention, seven versions are live and every one of them must be kept, backed up and restorable. - **"A read needs to know the current version."** A read needs the version its ciphertext names; the current version is a write-side concern only. The one-line model worth keeping: **a rotation is a change to the write path.** Everything the read path does afterwards is decided by what each ciphertext already says about itself.
- What breaks if the stored ciphertext does not record which key version produced it?You are left with two bad options: trial-decrypting each row against every live version until one succeeds, or keeping an external map from rows to versions that must stay correct for the entire retention period and survive every restore. Both make retiring a version guesswork, because nothing can prove which versions the corpus still depends on.
- A write path was still pinned to the previous key version after the rotation. How would you notice?Rows written after the rotation would still carry the old version marker. Grouping version markers by write date shows the gap immediately: the manager's current-version pointer says one thing and the rows produced since the rotation say another. Without that census the rotation looks successful, because nothing errors - the old version is perfectly valid for encryption until it is retired.
- Does anything about the read path have to change when a key is rotated?No, and that is the point of the marker. A reader passes the stored ciphertext and the manager resolves the version from it, so readers deployed before the rotation keep working unchanged. The read path only has to change when a version is retired or when a new version alters the algorithm, because then a reader may be asked for something it cannot do.
saying these in an interview costs you the question
- Thinks a rotation automatically re-encrypts the stored data
- Believes the previous version is destroyed when a new one is minted
- Says the new version retroactively protects rows written last year
- Cannot say how a reader learns which version to decrypt under
- Assumes every stored row must be readable under the newest version
- Confuses rotating a key with retiring the version before it