{"record":{"id":"0820862275b8091b","repo":"JuliusBrussee/caveman","slug":"envelope-open-w","errorCode":null,"errorMessage":"envelope: open: %w","messagePattern":"envelope: open: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/envelope/envelope.go","lineNumber":163,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: unwrap data key: %w\", err)\n\t}\n\tblock, err := aes.NewCipher(dataKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: aes: %w\", err)\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: gcm: %w\", err)\n\t}\n\tns := gcm.NonceSize()\n\tif len(ciphertext) < ns {\n\t\treturn nil, fmt.Errorf(\"envelope: ciphertext too short\")\n\t}\n\tnonce, ct := ciphertext[:ns], ciphertext[ns:]\n\tplaintext, err := gcm.Open(nil, nonce, ct, aad)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"envelope: open: %w\", err)\n\t}\n\treturn plaintext, nil\n}\n\nfunc scopeAAD(scope Scope) ([]byte, string, error) {\n\tscope.OrganizationID = strings.TrimSpace(scope.OrganizationID)\n\tscope.ProjectID = strings.TrimSpace(scope.ProjectID)\n\tscope.Kind = strings.TrimSpace(scope.Kind)\n\tif scope.OrganizationID == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"envelope: organization scope is required\")\n\t}\n\tif scope.Kind == \"\" {\n\t\treturn nil, \"\", fmt.Errorf(\"envelope: object kind is required\")\n\t}\n\taad, err := json.Marshal(struct {\n\t\tVersion        int    `json:\"version\"`\n\t\tOrganizationID string `json:\"organization_id\"`\n\t\tProjectID      string `json:\"project_id\"`","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/envelope/envelope.go#L145-L181","documentation":"GCM authenticated decryption failed in envelope.Open/OpenForScope: gcm.Open returned an error because the ciphertext, nonce, or additional authenticated data (AAD) does not match what was sealed. GCM is an authenticated mode, so any tampering, key mismatch, or scope mismatch surfaces as this wrapped error. The original plaintext is never exposed.","triggerScenarios":"Calling envelope.Open with the wrong data key (different keyring/key version), or envelope.OpenForScope with a Scope whose OrganizationID/ProjectID/Kind differ from those used at SealForScope time, or with ciphertext that was modified or corrupted in storage.","commonSituations":"Rotating keys without re-encrypting old rows; changing the scope kind string (e.g. \"doc\" vs \"document\") between seal and open; a migration edited encrypted bytes; an env pointed at a different KMS key than the one that sealed the data.","solutions":["Confirm the same data key / key reference used at seal time is being used at open time (check key version, keyring, env)","For OpenForScope, ensure the Scope (OrganizationID, ProjectID, Kind) exactly matches the one passed to SealForScope — AAD is bound via SHA-256","Re-seal the data with the current key/scope if the original credentials are unrecoverable","Check whether the stored ciphertext was altered (encoding round-trips, whitespace trimming, JSON escaping)"],"exampleFix":"// before: mismatched scope on open\npt, err := envelope.OpenForScope(ctx, key, ct, envelope.Scope{OrganizationID: orgID, Kind: \"note\"})\n// after: reuse the exact scope recorded at seal time\npt, err := envelope.OpenForScope(ctx, key, ct, envelope.Scope{OrganizationID: orgID, ProjectID: projID, Kind: \"document\"})","handlingStrategy":"try-catch","validationCode":"// cannot pre-validate integrity; ensure key ref and scope match what was used at seal time\n// store the key version and scope hash alongside the ciphertext","typeGuard":null,"tryCatchPattern":"pt, err := envelope.OpenForScope(ctx, key, ct, scope)\nif err != nil && strings.Contains(err.Error(), \"envelope: open:\") {\n    // wrong key or tampered/mismatched AAD: do not retry blindly;\n    // check key rotation history and scope equality before failing\n    return ErrDecryptionMismatch\n}","preventionTips":["Persist the key version / keyring ref next to each ciphertext","Reuse the exact same Scope struct value for seal and open (single constructor helper)","Treat any modification of encrypted blobs (trim, re-encode) as forbidden"],"tags":["go","crypto","aes-gcm","integrity-failure"],"backgroundTag":"checksum-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}