Re-sealing archives when algorithms age
An archive of historical blocks is read long after it is written. Its integrity rests on two primitives, and either can age:
| Layer | Primitive | Where |
|---|---|---|
| Content address | BLAKE3-256 multihash (0x1e) in every CID — the section bytes and the manifest | crates/archive/src/car.rs |
| Seal | SLH-DSA-SHAKE-256f over domain ‖ epoch ‖ root CID, one forward-secure key per epoch | crates/archive/src/seal.rs |
The rule this plan follows: never replace evidence, add to it. A re-seal wraps the old seal rather than discarding it, so each layer of the record stays checkable. A verifier who distrusts the new algorithm can still check the old one, for whatever it is still worth.
What counts as aging
Section titled “What counts as aging”Act on any of the following. The column says how urgent it is.
| Signal | Example | Urgency |
|---|---|---|
| NIST deprecation schedule for a parameter set | SP 800-131A-style transition dates for SLH-DSA-SHAKE-256f | Plan: reseal before the disallow date |
| A published attack that lowers a parameter set’s security below its category | a structural result against SHAKE256 or BLAKE3 | Reseal within one release cycle |
| A practical second-preimage or forgery | a demonstrated SLH-DSA forgery, or a BLAKE3 second preimage | Emergency: reseal now, and treat seals made after the attack date as suspect |
| The chain’s own crypto-agility policy retiring the suite | SuitePolicy deprecation window for 0x21 (crates/crypto-pq/src/agility/) | Follow the policy’s window |
A BLAKE3 collision is not a trigger for the content layer. An attacker who can produce collisions still cannot make an existing archive’s bytes hash to a different CID. It is a trigger for anything sealed after the attack became feasible, since such a batch could have been built with a twin.
Procedure A: the seal algorithm ages
Section titled “Procedure A: the seal algorithm ages”Content addresses are still sound; only SLH-DSA-SHAKE-256f is in doubt.
- Choose the successor suite in an ADR. It must be a registry suite
(
crates/crypto-pq/src/suite/) with NIST vectors intests/acvp_tests.rs, and not in the same family as the one being retired unless the break is parameter-specific. - Start a new key chain under that suite: a fresh genesis key, published out of band exactly as the first one was. Do not certify it from the old chain. Once the old algorithm is in doubt, a certificate it signed is worth no more than the old algorithm.
- Re-seal every archive root. Implemented in
crates/archive/src/reseal.rs: a re-seal signs"maya2c archive reseal v1" ‖ suite byte ‖ root ‖ encoded old evidence(lengths prefixed), which binds the new attestation to the old one rather than standing beside it. The old evidence stays inside the new, so layers nest across generations.reseal::schedulesays when: an archive is due once governance deprecates its outermost layer’s suite (maya_crypto_pq::agility), overdue at sunset, andreseal_if_duerefuses a successor that is not itself active. The successor key is a single published key, not an evolving chain: forward security covers the original SLH-DSA layer only. - Publish both chains’ transitions alongside the archives. A verifier checks the newest seal it trusts and may also check the older ones.
- Record the cut-over epoch in the archive index. From that point, seals under the old suite alone are refused for new archives.
Work: one signature per archive root, never a pass over the archive bytes. At SLH-DSA-SHAKE-256f’s size, that is about 49 KB of new signature per archive.
Procedure B: the content hash ages
Section titled “Procedure B: the content hash ages”BLAKE3 is in doubt, so CIDs are too.
- Choose the successor multihash in an ADR — SHA3-256 is the natural
one: already in the tree, and a different construction (a sponge, not
BLAKE3’s ChaCha-derived compression).
car.rsrefuses every multihash but BLAKE3-256 today, so this step includes teaching the reader the new code. - Re-address every archive. Read it, verify it under the old CIDs first, re-encode it with CIDs under the new multihash, and write it as a new archive. The block bytes are unchanged; only their names change.
- Seal the new root with a cross-reference:
domain ‖ epoch ‖ new root ‖ old root. That records that the sealer checked the old archive and that the new one is its re-addressing. - Keep the old archives until every consumer reads the new roots. Deleting them destroys the evidence that step 2 was honest.
Work: one full read and write of every archive, plus one seal each. Budget it like a re-sync.
Procedure C: a sealer key is compromised
Section titled “Procedure C: a sealer key is compromised”This is not an algorithm aging, but the key chain is built for it. Forward
security means epochs before the compromise stay sound: their keys were
erased, and the seed chain is one-way (seal.rs, pinned by
a_compromise_at_epoch_50_cannot_forge_the_past).
- Announce the compromise epoch
c. Verifiers must refuse every seal and transition at or aftercfrom that chain. - Start a new chain under the same suite, with a new genesis key published out of band.
- Re-seal the archives sealed at or after
c. Archives sealed beforecneed nothing.
What is not handled here
Section titled “What is not handled here”- Detection. Nothing in the tree watches for published attacks. The triggers above are a human decision, recorded in an ADR.
- Revocation distribution. Today a compromise announcement travels out of band. A transparency log for transitions and revocations is the obvious next step, and is not built.