{"record":{"id":"fb6b8c50b2cd5e54","repo":"gofiber/fiber","slug":"failed-to-decrypt-ciphertext-w","errorCode":null,"errorMessage":"failed to decrypt ciphertext: %w","messagePattern":"failed to decrypt ciphertext: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/encryptcookie/utils.go","lineNumber":89,"sourceCode":"\t}\n\n\tblock, err := aes.NewCipher(keyDecoded)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create AES cipher: %w\", err)\n\t}\n\n\tgcm, err := cipher.NewGCMWithRandomNonce(block)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create GCM mode: %w\", err)\n\t}\n\n\tif len(enc) < gcm.NonceSize()+gcm.Overhead() {\n\t\treturn \"\", ErrInvalidEncryptedValue\n\t}\n\n\tplaintext, err := gcm.Open(nil, nil, enc, []byte(name))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decrypt ciphertext: %w\", err)\n\t}\n\n\treturn string(plaintext), nil\n}\n\n// GenerateKey returns a random string of 16, 24, or 32 bytes.\n// The length of the key determines the AES encryption algorithm used:\n// 16 bytes for AES-128, 24 bytes for AES-192, and 32 bytes for AES-256-GCM.\nfunc GenerateKey(length int) string {\n\tif length != 16 && length != 24 && length != 32 {\n\t\tpanic(ErrInvalidKeyLength)\n\t}\n\n\tkey := make([]byte, length)\n\n\tif _, err := rand.Read(key); err != nil {\n\t\tpanic(err)\n\t}","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/encryptcookie/utils.go#L71-L107","documentation":"Returned by DecryptCookie when gcm.Open fails — the GCM authentication tag did not verify. This is the canonical 'wrong key, wrong cookie name, or tampered ciphertext' signal. The wrapped error is crypto/cipher's 'cipher: message authentication failed'.","triggerScenarios":"DecryptCookie(name, value, key) where the AES-GCM tag fails to verify: the key differs from the one used to encrypt, the cookie name (used as additional data) changed, the ciphertext/nonce bytes were modified, or the value was produced by a different GCM variant (NewGCM with explicit nonce vs NewGCMWithRandomNonce).","commonSituations":"Rotating the encryption key without invalidating sessions, renaming the cookie (the name is bound into the AAD), swapping EncryptCookie for a different GCM implementation, clients tampering with cookies, or copying a cookie value between environments with different keys.","solutions":["Confirm the same key that encrypted the cookie is being used to decrypt.","Confirm the cookie name passed to DecryptCookie matches the one passed to EncryptCookie (it is GCM additional data).","Ensure both sides use cipher.NewGCMWithRandomNonce (this middleware's nonce layout prepends the random nonce).","On key rotation, invalidate old cookies and force re-issue rather than attempting decrypt."],"exampleFix":"// before: name mismatch breaks AAD\nplaintext, _ := encryptcookie.DecryptCookie(\"session\", value, key)\n// after\nplaintext, err := encryptcookie.DecryptCookie(\"sess\", value, key)\nif err != nil {\n    c.ClearCookie(\"sess\")\n    return c.Redirect(\"/login\")\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"plain, err := encryptcookie.DecryptCookie(name, value, key)\nif err != nil {\n    // GCM auth failed: wrong key, wrong name, or tampered value\n    c.ClearCookie(name)\n    return c.Redirect(\"/login\")\n}","preventionTips":["Keep the cookie name identical on encrypt and decrypt (it is GCM additional data).","Rotate keys by invalidating old cookies, not by attempting cross-key decrypt.","Use cipher.NewGCMWithRandomNonce consistently (this middleware's layout)."],"tags":["crypto","aes-gcm","encryptcookie","authentication","tampering"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}