phalcon/cphalcon · error · EncryptionFailed

Could not encrypt data

Error message

Could not encrypt data

What it means

Crypt's encrypt path calls openssl_encrypt(); a false return is converted to EncryptionFailed('Could not encrypt data'). OpenSSL refuses to encrypt when the key length does not match the cipher (e.g., a 13-byte string with aes-256 requiring 32), or when GCM/CCM is used with invalid tag/length parameters - Phalcon surfaces it as this exception.

Source

Thrown at phalcon/Encryption/Crypt.zep:924

                iv,
                authTag,
                authData,
                authTagLength
            );

            let this->authTag = authTag;
        } else {
            let encrypted = openssl_encrypt(
                padded,
                cipher,
                encryptKey,
                OPENSSL_RAW_DATA,
                iv
            );
        }

        if (false === encrypted) {
            throw new EncryptionFailed();
        }

        /**
         * Store the tag with encrypted data and return it. In the non AEAD
         * mode this is an empty string
         */
        return encrypted . authTag;
    }

    /**
     * Initialize available cipher algorithms.
     *
     * @return static
     * @throws Exception
     */
    protected function initializeAvailableCiphers() -> <static>
    {
        var available, cipher;

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Derive an exact-length key: 32 bytes for aes-256-* - e.g. hash('sha256', $secret, true) - or generate with random_bytes(32) and store verbatim.
  2. Validate key length at boot: if (strlen($key) !== 32) throw ... next to your Crypt setup.
  3. For GCM/CCM confirm setAuthData() was called and the tag length (if set) is 4..16.
  4. Catch EncryptionFailed where user data is encrypted and degrade gracefully (log, reject the operation) - never retry with a different key silently.

Example fix

// before
$crypt->setKey(getenv('APP_SECRET'));   // arbitrary length, e.g. 19 chars
$crypt->encrypt($data);                 // openssl_encrypt false -> throws

// after
$crypt->setKey(substr(hash('sha256', getenv('APP_SECRET'), true), 0, 32));
$crypt->encrypt($data);
Defensive patterns

Strategy: try-catch

Validate before calling

$required = ['aes-128' => 16, 'aes-192' => 24, 'aes-256' => 32];
$prefix = substr($cipher, 0, 7);
if (isset($required[$prefix]) && strlen($key) !== $required[$prefix]) {
    throw new \RuntimeException("Key for {$cipher} must be {$required[$prefix]} bytes");
}
$crypt->setKey($key);
$crypt->encrypt($data);

Try / catch

try {
    $stored = $crypt->encrypt($data);
} catch (\Phalcon\Encryption\Crypt\Exception\EncryptionFailed $e) {
    // wrong key length or AEAD misconfiguration - fail the write, alert
    $logger->error('Encryption failed: ' . $e->getMessage());
    throw new \RuntimeException('Could not secure payload', 0, $e);
}

Prevention

When it happens

Trigger: setKey($passphrase) with an arbitrary-length string while the cipher expects an exact key size; AEAD mode with an incompatible auth tag setup; extremely rare OpenSSL build issues with the chosen cipher (usually caught earlier by the availability check).

Common situations: Passing human passwords directly as keys; env key values that lost bytes (encoding, quoting) so their length no longer matches; mixing key derivation on one side but not the other; truncated keys after config serialization.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/fcaefa3a7b568686. Report an issue: GitHub.