{"record":{"id":"66c214f35fd33b1d","repo":"nats-io/nats-server","slug":"unable-to-unseal-data-v","errorCode":null,"errorMessage":"unable to unseal data: %v","messagePattern":"unable to unseal data: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/tpm/js_ek_tpm_windows.go","lineNumber":197,"sourceCode":"\t// Load the public/private blobs into the TPM for decryption.\n\tobjectHandle, _, err := tpm2.Load(rwc, srkHandle, srkPassword, publicBlob, privateBlob)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to load data: %v\", err)\n\t}\n\tdefer tpm2.FlushContext(rwc, objectHandle)\n\n\t// Create the authorization session with TPM.\n\tsessHandle, _, err := policyPCRPasswordSession(rwc, pcr)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to get auth session: %v\", err)\n\t}\n\tdefer func() {\n\t\ttpm2.FlushContext(rwc, sessHandle)\n\t}()\n\t// Unseal the data we've loaded into the TPM with the object (js key) password.\n\tunsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to unseal data: %v\", err)\n\t}\n\treturn string(unsealedData), nil\n}\n\n// Returns session handle and policy digest.\nfunc policyPCRPasswordSession(rwc io.ReadWriteCloser, pcr int) (sessHandle tpmutil.Handle, policy []byte, retErr error) {\n\tsessHandle, _, err := tpm2.StartAuthSession(\n\t\trwc,\n\t\ttpm2.HandleNull,  /*tpmKey*/\n\t\ttpm2.HandleNull,  /*bindKey*/\n\t\tmake([]byte, 16), /*nonceCaller*/\n\t\tnil,              /*secret*/\n\t\ttpm2.SessionPolicy,\n\t\ttpm2.AlgNull,\n\t\ttpm2.AlgSHA256)\n\tif err != nil {\n\t\treturn tpm2.HandleNull, nil, fmt.Errorf(\"unable to start session: %v\", err)\n\t}","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/tpm/js_ek_tpm_windows.go#L179-L215","documentation":"The key object was loaded and the policy session created, but tpm2.UnsealWithSession failed to unseal the data. Because the policy binds both PCR state and password, this most often means the PCR value changed since sealing or the object password is wrong.","triggerScenarios":"tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword) errors — jsKeyPassword mismatch, PCR state differs from seal time, or policy not satisfied.","commonSituations":"A firmware/OS update changed PCR values (e.g. PCR 0/7) after the key was sealed; wrong jsKeyPassword passed by caller; TPM HMAC/authorization failure.","solutions":["Compare current PCR digest with the one at seal time (tpm2_pcrread); if changed, re-seal the key.","Verify jsKeyPassword/objectPassword matches the one used at seal time.","As a last resort, delete the key file and regenerate the encryption key (data encrypted with the old key becomes unrecoverable).","Back up the key file path mapping to the PCR index used at seal time."],"exampleFix":"// before: wrong password silently propagated\nunsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword)\n// after: fail with actionable context\nunsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"unable to unseal data (check password and PCR %d state): %v\", pcr, err)\n}","handlingStrategy":"try-catch","validationCode":"currentPCR, err := tpm2.ReadPCR(rwc, pcr, tpm2.AlgSHA256)\nif err != nil {\n\treturn fmt.Errorf(\"cannot read PCR %d: %w\", pcr, err)\n}\n// compare with PCR digest recorded at seal time before attempting unseal","typeGuard":"func isUnsealError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"unable to unseal data\")\n}","tryCatchPattern":"key, err := LoadJetStreamEncryptionKeyFromTPM(rwc)\nif err != nil && isUnsealError(err) {\n\tlog.Printf(\"unseal failed (PCR %d state or password changed): %v\", pcr, err)\n\t// DO NOT delete key file automatically — encrypted data may be lost\n\treturn err\n}","preventionTips":["Pin the sealed key to a PCR that changes rarely (not PCR 0/7 if firmware updates are frequent).","Store the PCR digest at seal time and compare before unsealing.","Manage jsKeyPassword centrally so seal/unseal always match.","Never reboot into an updated OS/firmware before backing up the key dependency chain."],"tags":["tpm","windows","unseal","pcr","go"],"backgroundTag":"tpm-unseal-failed","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}