digininja/DVWA · error · Exception

Decryption failed

Error message

Decryption failed

What it means

openssl_decrypt returned false for aes-256-gcm. GCM is authenticated encryption: the last 16 bytes of the token are the auth tag, and decryption fails rather than returning garbage whenever tag verification fails - wrong key, wrong nonce, or any single-bit change in ciphertext or tag. At the impossible level this is by design: tampered or forged tokens cannot decrypt.

Source

Thrown at vulnerabilities/cryptography/source/token_library_impossible.php:31

	$e = openssl_encrypt($plaintext, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
	if ($e === false) {
		throw new Exception ("Encryption failed");
	}
	return $e . $tag;
}

function decrypt ($ciphertext, $iv) {
	if (strlen ($iv) != 12) {
		throw new Exception ("IV must be 12 bytes, " . strlen ($iv) . " passed");
	}

    $tag = substr($ciphertext, -16);
	$text = substr($ciphertext, 0, -16);

	$e = openssl_decrypt($text, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
	if ($e === false) {
		throw new Exception ("Decryption failed");
	}
	return $e;
}

// Added the debug flag so that when calling from the script
// the function can print the data used to create the token

function create_token () {
	$token = "userid:2";
	$iv = openssl_random_pseudo_bytes(12, $cstrong);

	$e = encrypt ($token, $iv);
	$data = array (
					"token" => base64_encode ($e),
					"iv" => base64_encode ($iv),
				);
	return json_encode($data);
}

View on GitHub (pinned to 5d5c76cced)

Solutions

  1. Submit the token exactly as issued to confirm the happy path works - create_token() output must decrypt.
  2. Treat this exception as authentication failure: reject the token and log, do not attempt to 'fix' the bytes.
  3. Inspect openssl_error_string() - it reports the tag verification failure explicitly.
  4. If legitimately re-encrypting, use the same KEY, aes-256-gcm, a fresh 12-byte nonce, and concatenate ciphertext . tag exactly as encrypt() does before base64-encoding.

Example fix

// before
$e = openssl_decrypt($text, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
if ($e === false) {
    throw new Exception ("Decryption failed");
}
// after
$e = openssl_decrypt($text, ALGO, KEY, OPENSSL_RAW_DATA, $iv, $tag);
if ($e === false) {
    throw new Exception("Decryption failed: GCM tag verification failed (tampered token or wrong key/IV)");
}
Defensive patterns

Strategy: try-catch

Validate before calling

$iv = base64_decode($data_array['iv'], true);
$raw = base64_decode($data_array['token'], true);
if ($iv === false || strlen($iv) !== 12 || $raw === false || strlen($raw) <= 16) {
    // token must be ciphertext (>= 1 byte) + 16-byte tag; reject malformed input early
    return json_encode(['status' => 523, 'message' => 'Malformed token']);
}

Type guard

function isGcmTokenShape(string $rawCiphertext, string $iv): bool
{
    return strlen($iv) === 12 && strlen($rawCiphertext) > 16;
}

Try / catch

try {
    $d = decrypt($ciphertext, $iv);
} catch (Exception $e) {
    // GCM failure == authentication failure: reject the token, never retry with tweaks
    error_log('GCM verification failed: ' . $e->getMessage());
    $ret = ['status' => 526, 'message' => 'Unable to decrypt token'];
}

Prevention

When it happens

Trigger: Modifying any byte of the base64 token or iv field; reusing a 12-byte nonce from a different token; re-encrypting with a different key; truncating the token so substr($ciphertext, -16) slices the wrong bytes; submitting CBC ciphertext (which trips the IV-length check first at line 23).

Common situations: Token forgery attempts (the lab's exercise); key mismatch between the issuing and verifying services; nonce-reuse bugs in custom token code; non-strict base64_decode producing different bytes than encoded.

Related errors


AI-assisted analysis of digininja/DVWA@5d5c76cced (2026-08-21). Data as JSON: /api/errors/2140ebac3c96b807. Report an issue: GitHub.