phalcon/cphalcon · error · InvalidDecryptLength

The provided input is too short for the selected cipher.

Error message

The provided input is too short for the selected cipher.

What it means

Before decrypting, Crypt verifies the ciphertext is at least as long as the cipher's IV (openssl_cipher_iv_length for the configured cipher). Input shorter than that cannot possibly contain IV + data, so it throws InvalidDecryptLength without attempting decryption. This guards the mb_substr() extraction of the IV from producing garbage or an empty string.

Source

Thrown at phalcon/Encryption/Crypt.zep:239

        var blockSize, cipher, cipherText, decrypted, decryptKey, digest,
            hashAlgorithm, hashLength, iv, ivLength, mode;

        let decryptKey = this->key;
        if true !== empty(key) {
            let decryptKey = key;
        }

        if true === empty(decryptKey) {
            throw new EmptyDecryptionKey();
        }

        let cipher   = this->cipher,
            ivLength = this->ivLength;

        this->checkCipherHashIsAvailable(cipher, "cipher");

        if true !== this->isValidDecryptLength(input) {
            throw new InvalidDecryptLength();
        }

        let mode      = this->getMode(),
            blockSize = this->getBlockSize(mode),
            iv        = mb_substr(input, 0, ivLength, "8bit");

        /**
         * Check if we have chosen signing and use the hash
         */
        let digest        = "",
            hashAlgorithm = this->getHashAlgorithm();
        if true === this->useSigning {
            if !fetch hashLength, this->hashLengthCache[hashAlgorithm] {
                let hashLength = strlen(this->phpHash(hashAlgorithm, "", true));
                let this->hashLengthCache[hashAlgorithm] = hashLength;
            }
            let digest     = mb_substr(input, ivLength, hashLength, "8bit"),
                cipherText = mb_substr(input, ivLength + hashLength, null, "8bit");

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Match the API pair: if data was produced with encryptBase64(), decrypt with decryptBase64().
  2. Sanity-check before calling: $crypt->isValidDecryptLength($input) (public method) - it applies exactly this check.
  3. Transport ciphertext as base64 (use encryptBase64) or raw-binary-safe (POST body, not URL params) to prevent truncation/corruption.
  4. If input is legitimately empty (optional field), branch on empty($input) and skip decryption instead of calling decrypt('').

Example fix

// before
$plain = $crypt->decrypt($request->getQuery('t', 'string')); // truncated by URL transport

// after
$token = $request->getQuery('t', 'string');
if ($token === null || !$crypt->isValidDecryptLength($token)) {
    throw new \InvalidArgumentException('Malformed or missing token');
}
$plain = $crypt->decrypt($token);
Defensive patterns

Strategy: validation

Validate before calling

if ('' === $payload || !$crypt->isValidDecryptLength($payload)) {
    throw new \InvalidArgumentException('Payload missing or shorter than the cipher IV');
}
$plain = $crypt->decrypt($payload);

Type guard

function isPlausibleCipherText(\Phalcon\Encryption\Crypt $crypt, string $payload): bool
{
    return '' !== $payload && $crypt->isValidDecryptLength($payload);
}

Try / catch

try {
    $plain = $crypt->decrypt($payload);
} catch (\Phalcon\Encryption\Crypt\Exception\InvalidDecryptLength $e) {
    // treat as malformed input, not a 500 - reject the request/token
    return $response->setStatusCode(400, 'Bad Request')->setContent('Invalid token');
}

Prevention

When it happens

Trigger: Calling decrypt() on: an empty string; a truncated payload; data that was never encrypted by this class (plain text, JSON); a base64 string when you meant to use decryptBase64(); or ciphertext produced with a different cipher whose IV length is larger.

Common situations: Passing base64-wrapped payloads (Crypt produces raw binary; encryptBase64/decryptBase64 are the matching pair) - base64 text of a short message can still be longer than the IV and instead fail later with Mismatch, but a short one fails here. Empty DB columns, truncated URL parameters (binary data mangled by GET transport), and switching ciphers between encrypt and decrypt are typical causes.

Related errors


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