@@ -121,7 +121,6 @@ const TOKEN_CHECKSUM_KEY = Buffer.from('sealed-env:token-checksum:v1', 'utf8');
121121const VAULT_ID_PREFIX = Buffer . from ( 'sealed-env:vault-id:v1' , 'utf8' ) ;
122122const DEPLOY_SIG_INFO = Buffer . from ( 'sealed-env:deploy-sig:v1' , 'utf8' ) ;
123123const EPOCH_INFO = Buffer . from ( 'epoch-v1' , 'utf8' ) ;
124- const UNSEAL_TOKEN_KEY_INFO = Buffer . from ( 'sealed-env:unseal-token-key:v1' , 'utf8' ) ;
125124
126125// scrypt params — match sealed-env 0.1.1 default (SEC-002 OWASP 2024 floor).
127126// Fixtures pin these so any stack can re-derive ek byte-identically.
@@ -218,7 +217,6 @@ function buildTierBToken({ masterKey, salt, exp, nonce, overrideVaultId }) {
218217// byte string. Reading and writing is lossless (§11.6).
219218function buildUnsealToken ( {
220219 masterKey,
221- signingKey,
222220 totpSecret,
223221 salt,
224222 iss = 'sealed-env-cli' ,
@@ -229,15 +227,15 @@ function buildUnsealToken({
229227} ) {
230228 // enterprise_epoch is bound to (totpSecret, salt) per SPEC §9.
231229 const enterpriseEpoch = hmacSha256 ( totpSecret , Buffer . concat ( [ salt , EPOCH_INFO ] ) ) ;
232- // The legacy JWS unseal-token-key is HKDF( derived_key, ...). We use the
233- // signingKey as an additional ingredient to bind team/enterprise scope.
234- const derivedKey = scryptDerive ( masterKey , salt ) ;
235- const tokenKey = hkdfSha256 (
236- Buffer . concat ( [ derivedKey , signingKey ] ) ,
237- salt ,
238- UNSEAL_TOKEN_KEY_INFO ,
239- 32 ,
240- ) ;
230+ // The legacy JWS unseal-token-key is ` derived_key` directly, per SPEC §11.6
231+ // u-mode ("HMAC-SHA256(derived_key, base64url(header)+'.'+base64url(payload))")
232+ // and verified by node/src/totp/unsealToken.ts:98 + :139. A previous draft of
233+ // this generator HKDF'd derivedKey||signingKey into a separate tokenKey; that
234+ // was a fabrication, not the contract. signingKey participates in the wider
235+ // sealed-file HMAC (SPEC §6) but does NOT enter the unseal-token signing
236+ // input. (Use raw derived_key here, not a fresh HMAC — the runtime verifier
237+ // computes `hmacSha256(derivedKey, signingInput)` so this side must match.)
238+ const tokenKey = scryptDerive ( masterKey , salt ) ;
241239 // Construct the JWS header + payload that the legacy verifier would
242240 // sign. Sig is HMAC over base64url(header)+"."+base64url(payload).
243241 const header = { alg : 'HS256' , typ : 'sealed-env-unseal/v1' } ;
@@ -330,7 +328,6 @@ function runSelfChecks() {
330328 // 5. u-mode token mint is deterministic with fixed inputs (no Date.now()).
331329 const u1 = buildUnsealToken ( {
332330 masterKey : MASTER_KEY ,
333- signingKey : SIGNING_KEY ,
334331 totpSecret : TOTP_SECRET ,
335332 salt : SALT ,
336333 iat : 1767225600 ,
@@ -340,7 +337,6 @@ function runSelfChecks() {
340337 } ) ;
341338 const u2 = buildUnsealToken ( {
342339 masterKey : MASTER_KEY ,
343- signingKey : SIGNING_KEY ,
344340 totpSecret : TOTP_SECRET ,
345341 salt : SALT ,
346342 iat : 1767225600 ,
@@ -479,7 +475,6 @@ function unsealValid() {
479475 // iat/exp/ops_id are fixed so the token doesn't depend on Date.now().
480476 const token = buildUnsealToken ( {
481477 masterKey : MASTER_KEY ,
482- signingKey : SIGNING_KEY ,
483478 totpSecret : TOTP_SECRET ,
484479 salt : SALT ,
485480 iat : 1767225600 , // 2026-01-01T00:00:00Z
0 commit comments