{"record":{"id":"6d552f36bd941a85","repo":"phalcon/cphalcon","slug":"could-not-decrypt-data","errorCode":null,"errorMessage":"Could not decrypt data","messagePattern":"Could not decrypt data","errorType":"exception","errorClass":"DecryptionFailed","httpStatus":null,"severity":"error","filePath":"phalcon/Encryption/Crypt.zep","lineNumber":838,"sourceCode":"                cipher,\n                decryptKey,\n                OPENSSL_RAW_DATA,\n                iv,\n                authTag,\n                authData\n            );\n        } else {\n            let decrypted = openssl_decrypt(\n                cipherText,\n                cipher,\n                decryptKey,\n                OPENSSL_RAW_DATA,\n                iv\n            );\n        }\n\n        if (false === decrypted) {\n            throw new DecryptionFailed();\n        }\n\n        return decrypted;\n    }\n\n    /**\n     * @param string $mode\n     * @param string $input\n     * @param int    $blockSize\n     *\n     * @return string\n     * @throws Exception\n     */\n    protected function encryptGetPadded(\n        string mode,\n        string input,\n        int blockSize\n    ) -> string {","sourceCodeStart":820,"sourceCodeEnd":856,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Encryption/Crypt.zep#L820-L856","documentation":"Crypt's internal decryptGcmCcm/decryptCbc path calls openssl_decrypt(); when it returns false (OpenSSL could not decrypt), Phalcon throws DecryptionFailed('Could not decrypt data'). Unlike Mismatch (signing), this is OpenSSL itself rejecting the input: key length not matching the cipher, corrupted ciphertext/IV, or mode/cipher mismatch.","triggerScenarios":"Decrypting with a key of the wrong length for the cipher (e.g., a 16-byte passphrase with aes-256); ciphertext or IV bytes corrupted/truncated (but still >= ivLength so the length check passed); data encrypted with a different cipher or by another library; GCM tag bytes lost because storage stripped trailing bytes.","commonSituations":"Using a human password directly as the key instead of a derived 32-byte key; DB columns/base64 layers dropping trailing NUL bytes from the appended auth tag; switching ciphers after data was stored; decrypting data from an external system with a different padding/signing scheme.","solutions":["Use keys of the exact byte length the cipher requires (32 bytes for aes-256-*); derive from passwords with hash('sha256', $password, true) or PBKDF2/argon2 rather than passing the password raw.","Ensure the payload reaches decrypt() byte-identical: store base64 (encryptBase64/decryptBase64) or BLOB-safe columns, never URL params or trimmed strings.","Verify cipher, mode, and auth-tag handling match the encrypt side (for GCM, the tag is appended to the ciphertext by Phalcon - keep storage big enough).","Catch DecryptionFailed and Mismatch together at the call site and treat both as 'unreadable payload'."],"exampleFix":"// before\n$crypt->setKey('my-password');                 // 11 bytes, wrong for aes-256\n$plain = $crypt->decrypt($blob);               // openssl_decrypt -> false -> throws\n\n// after\n$crypt->setKey(hash('sha256', 'my-password', true)); // exact 32 bytes\n$plain = $crypt->decrypt($blob);","handlingStrategy":"try-catch","validationCode":"// Pre-flight the most common cause: exact key length for the cipher\n$keyLengths = ['aes-128' => 16, 'aes-192' => 24, 'aes-256' => 32];\n$prefix = substr($crypt->getCipher(), 0, 7); // e.g. 'aes-256'\nif (isset($keyLengths[$prefix]) && strlen($crypt->getKey()) !== $keyLengths[$prefix]) {\n    throw new \\RuntimeException(\"Key must be {$keyLengths[$prefix]} bytes for {$crypt->getCipher()}\");\n}","typeGuard":null,"tryCatchPattern":"use Phalcon\\Encryption\\Crypt\\Exception\\DecryptionFailed;\nuse Phalcon\\Encryption\\Crypt\\Exception\\Mismatch;\n\ntry {\n    $plain = $crypt->decrypt($payload);\n} catch (DecryptionFailed | Mismatch $e) {\n    // unreadable payload: wrong key/length or corrupted bytes - reject, never echo raw\n    $logger->warning('Undecryptable payload: ' . $e->getMessage());\n    throw new AppException\\UnrecoverablePayload('Stored data cannot be decrypted', 0, $e);\n}","preventionTips":["Derive keys to the cipher's exact byte length (32 bytes for aes-256) and assert the length at boot.","Round-trip test encrypt/decrypt in CI for every environment/key pair.","Persist ciphertext as base64/BLOB to prevent truncation of IV or GCM tag bytes."],"tags":["phalcon","crypt","decryption","openssl","key-length","data-corruption"],"backgroundTag":"decryption-failed","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}