{"record":{"id":"4e01b6647117a8dc","repo":"cloudflare/cloudflared","slug":"failed-to-decrypt-message","errorCode":null,"errorMessage":"failed to decrypt message","messagePattern":"failed to decrypt message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/encrypt.go","lineNumber":82,"sourceCode":"\n// Decrypt data that was encrypted using our publicKey. It will use our privateKey and the sender's publicKey to decrypt\n// data is an encrypted buffer of data, mostly like from the Encrypt function. Messages contain the nonce data on the front\n// of the message.\n// senderPublicKey is a base64 encoded version of the sender's public key (most likely from the PublicKey function).\n// The return value is the decrypted buffer or an error.\nfunc (e *Encrypter) Decrypt(data []byte, senderPublicKey string) ([]byte, error) {\n\tif len(data) < 24 {\n\t\treturn nil, errors.New(\"message is too short to contain a nonce\")\n\t}\n\tvar decryptNonce [24]byte\n\tcopy(decryptNonce[:], data[:24]) // we pull the nonce from the front of the actual message.\n\tpubKey, err := e.decodePublicKey(senderPublicKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdecrypted, ok := box.Open(nil, data[24:], &decryptNonce, pubKey, e.privateKey)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to decrypt message\")\n\t}\n\treturn decrypted, nil\n}\n\n// decodePublicKey will base64 decode the provided key to the box representation\nfunc (e *Encrypter) decodePublicKey(key string) (*[32]byte, error) {\n\tpub, err := base64.URLEncoding.DecodeString(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar newKey [32]byte\n\tcopy(newKey[:], pub)\n\treturn &newKey, nil\n}\n","sourceCodeStart":64,"sourceCodeEnd":97,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/encrypt.go#L64-L97","documentation":"Decrypt uses NaCl box (XSalsa20-Poly1305) to decrypt a payload with the sender's public key and the local private key. box.Open returns ok=false when authentication fails, meaning the ciphertext, nonce, or key pair does not match what was used to encrypt. The library converts that failure into this generic error rather than leaking crypto internals.","triggerScenarios":"Calling Encrypter.Decrypt(senderPublicKey, ciphertext) with data whose last segment fails Poly1305 verification: ciphertext encrypted with a different sender key, a corrupted or truncated payload, the wrong private key loaded, or the first 24-byte nonce prefix split incorrectly.","commonSituations":"Base64 keys copied from the wrong token or service (sender/public key mismatch), payloads mangled by passing through a non-binary-safe channel (e.g. trimming base64, CRLF translation), rotating keypairs on one side only, or decrypting a payload that was encrypted with a different version of the token format.","solutions":["Verify the senderPublicKey passed to Decrypt is exactly the base64 public key of the party that encrypted the payload.","Confirm the Encrypter's privateKey corresponds to the recipient public key used at encryption time; re-check which key file/env value is loaded.","Ensure the full raw output of Encrypt (nonce prefix + ciphertext) is passed to Decrypt unmodified; check encoding/decoding steps (base64, JSON, HTTP bodies) for corruption or trimming.","Regenerate a fresh keypair pair and do a round-trip test (Encrypt then Decrypt with the new pair) to isolate stale-key issues."],"exampleFix":"// before: using a mismatched key from config\n decrypted, err := e.Decrypt(cfg.OldServicePublicKey, payload)\n// after: use the public key of the actual sender\n decrypted, err := e.Decrypt(senderKey.Public(), payload)","handlingStrategy":"try-catch","validationCode":"if senderPublicKey == \"\" || len(payload) < 24+box.Overhead {\n    return fmt.Errorf(\"payload too short or sender public key missing\")\n}","typeGuard":"func canDecrypt(pub *[32]byte, payload []byte) bool {\n    return pub != nil && len(payload) >= 24+box.Overhead\n}","tryCatchPattern":"decrypted, err := e.Decrypt(senderPublicKey, payload)\nif err != nil {\n    if err.Error() == \"failed to decrypt message\" {\n        // log sender key fingerprint + payload hash, re-fetch keys and retry once\n    }\n    return fmt.Errorf(\"decrypting token payload: %w\", err)\n}","preventionTips":["Store and pass sender keys together with the encrypted payloads they correspond to (keyed envelopes).","Use base64 (not raw bytes) for all transport of nonce+ciphertext and validate length before decrypting.","Round-trip test every keypair rotation: encrypt with new, decrypt with new, before retiring old keys.","Log a hash of the payload and key fingerprint on failure to spot which side is mismatched."],"tags":["crypto","nacl-box","decryption","token"],"backgroundTag":"checksum-mismatch","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}