Repository navigation
Seal CRX signing keys under the publisher's passphrase - #172
Merged
Merged
Conversation
…le present The store held every publisher's CRX signing private key under one CRX_KEY_SECRET (scrypt, fixed salt) and packed a .crx with it on every public download. Root on the box = every extension identity. Now (seal v2): - Each publisher sets a signing passphrase in the store (seal.js, WebCrypto: PBKDF2-SHA256 600k + HKDF -> KEK wraps a random data key; one-time recovery key). The store keeps only that record. - Signing keys are sealed under the data key (AES-256-GCM, AAD = publisher + extension). The browser/script derives the data key and sends it, never the passphrase, only with the request that needs the key: create a key, migrate one, or sign a release (POST /versions with dek, POST /extensions/:id/sign). - The .crx is packed in that request and stored (extension_signed_crx); download.crx and updates.xml serve the last signed release. Nothing packs on download any more. - Migration: setting the passphrase re-seals the publisher's legacy keys in the same request and signs each listing's latest release. LEGACY_KEY_MIGRATION=off is the cutoff; prepack-legacy.js packs current releases of unmigrated listings once so installs keep working. - store-publish.mjs signs when TRON_SIGNING_PASSPHRASE is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| const rraw = rand(32); | ||
| const recoveryKey = encodeRecovery(rraw); | ||
| rraw.fill(0); | ||
| const kdf = { alg: 'PBKDF2-SHA256', iterations: ITER, salt: b64(rand(16)) }; |
Contributor
ThreatCrush Security Scan51 finding(s) HIGH/CRITICAL: 2 | MEDIUM: 28 | LOW: 21
…and 1 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
The store held every publisher's CRX signing private key under one
CRX_KEY_SECRET(scrypt, fixed salt), and packed a .crx with it on every public download.Now
apps/extensions/public/seal.js(WebCrypto) runs PBKDF2-SHA256 600k + HKDF to get a KEK. The KEK wraps a random data key, and a one-time recovery key wraps it too. The store keeps only this record (publisher_key_seals).POST /extensions/:id/versionswithdek, orPOST /extensions/:id/sign.extension_signed_crx).download.crx,updates.xmland/downloadserve the last signed release. Nothing is packed on download any more.POST /publisher/sealre-seals the publisher's legacy keys in the same request, then signs each listing's latest release.LEGACY_KEY_MIGRATION=offis the cutoff.bun /api/dist/store/prepack-legacy.jspacks the current release of unmigrated listings once, so installs keep working until the publisher sets a passphrase.store-publish.mjssigns whenTRON_SIGNING_PASSPHRASEis set.Tests:
signing.test.tscovers the browser seal accepted by the server, AAD binding, no env fallback, and migration with the switch on and off. The store suite passes 58/58.src/mcp/tron.test.tsfails before and after this change because@tronbrowser/sdkisn't built locally.🤖 Generated with Claude Code