Repository navigation
fix(release): root-init at 600,000 PBKDF2 iterations; root custody as decided - #244
Merged
Merged
Conversation
…s; custody as decided root-init wrote the root as PBES2 with PBKDF2-HMAC-SHA256 at 2,048 iterations, Node's KeyObject.export default, which has no option for more. scripts/release-keys/pkcs8.ts now writes the PBES2 EncryptedPrivateKeyInfo itself (RFC 8018, RFC 5958): PBKDF2-HMAC-SHA256 at 600,000 iterations (OWASP), a 16-byte salt, AES-256-CBC, the same form as `openssl pkcs8 -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256 -iter 600000`. Node and OpenSSL read it with the passphrase; sign-keys still reads the older 2,048-iteration roots. readPbes2 parses the parameters back from the DER, and a test asserts the written file's iteration count, prf and cipher, and that an old-form root still signs a keys.json the consumer verifies. Docs (owner decision, 2026-11-15): the root's encrypted file has two sealed copies, one in the private cloudhal-dev/key-custody repository (owner only; no Actions, apps or deploy keys; no passphrase, no plaintext key) and one offline; the passphrase is kept apart, in a password manager and on sealed paper. That copy is the one documented exception to "no copy in a repository or a cloud service", which now names the release key, the yank key and passphrases. The plaintext yank key and the yank-list deploy key were deleted once set as release-yank secrets; a lost one is rotated, not restored. The runbook gives the by-hand re-encryption for roots made before this fix, and RFC-0046 §9's custody line carries the same exception. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
…assword manager The custody rule said no passphrase is ever in a cloud service, yet the root passphrase is kept in a password manager, and most sync. Both the key doc and RFC-0046 §9 now say no passphrase is ever in a repository, a chat, an issue, or a cloud service other than an end-to-end encrypted password manager, and the root's passphrase is kept in such a password manager. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Madraka <[email protected]>
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.
Two follow-ups to the trust ceremony (RFC-0046 §9). Scripts and docs only, so no changeset.
1.
root-initKDFBefore:
root-initwrote the root as PBES2 with PBKDF2-HMAC-SHA256 at 2,048 iterations. That is the default of Node'sKeyObject.export({ cipher, passphrase }), and the API has no option for more.Now: the new
scripts/release-keys/pkcs8.tswrites the PBES2EncryptedPrivateKeyInfoitself (RFC 8018, RFC 5958):ROOT_KDF_ITERATIONS, OWASP);This is the same form as
openssl pkcs8 -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256 -iter 600000.Reading roots:
sign-keysis unchanged:createPrivateKey({ key, passphrase })reads both the new and the old 2,048-iteration roots.readPbes2parses the PBES2/PBKDF2 parameters back out of the DER.Tests (
packages/cli/test/release-keys.test.ts):{ iterations: 600000, saltBytes: 16, prf: "hmacWithSHA256", cipher: "aes-256-cbc" }.readPbes2reads as 2,048 iterations) still signs akeys.jsonthat the consumer'sverifyKeysaccepts.Checked by hand with OpenSSL 4.1:
openssl asn1parseshowsPBES2 / PBKDF2 / INTEGER 0927C0 / hmacWithSHA256 / aes-256-cbc, andopenssl pkey -passinopens the file.2. Custody text (owner decision, 2026-11-15)
docs/security/release-signing-keys.md, Root keys › Custody:cloudhal-dev/key-custody(owner only; no Actions, apps or deploy keys; no passphrase and no plaintext key in it);asn1parse, and records thatcloudhal-root-2026awas re-encrypted on 2026-11-15.key-custodyas the one exception.yank-listdeploy key..peminstead of "move to offline storage".Tests run, one at a time
packages/cli: release-keys (22), release-workflows (14), release-trust-assets (4), yank-list-workflow (6), deploy-release (24).packages/core-contracts: release-schemas (23).packages/release-verify: package (16), trust.tscinpackages/cli(it type-checks the scripts it imports), biome, andpnpm size:check.🤖 Generated with Claude Code