* * One of them has been encrypted with ECB. * * Detect it. * * Remember that the problem with ECB is that it is stateless and deterministic; the same 16 byte plaintext block will always produce the same 16 byte ciphertext. */ function repeatedBlockCount($data) { $dataLen = strlen($data); $repetitions = 0; for ($i = 0; $i < $dataLen; $i += 16) { $block = substr($data, $i, 16); $repetition = strpos($data, $block, $i + 16); if ($repetition && $repetition % 16 === 0) { $repetitions++; } } return $repetitions; } // don't output if we're included into another script. if (!debug_backtrace()) { $data = array_map('hex2bin', file('08-data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); foreach ($data as $k => $encrypted) { $repetitions = repeatedBlockCount($encrypted); if ($repetitions) { $line = $k + 1; print "String at on line {$line} has repeated blocks (probable ECB)\n"; } } }
*/ require_once '../utils/random-bytes.php'; require_once '../01-basics/07-aes-in-ecb-mode.php'; require_once '10-implement-cbc-mode.php'; function randomlyEncryptECBorCBC($data) { $key = getRandomBytes(16); $pad1 = getRandomBytes(rand(5, 10)); $pad2 = getRandomBytes(rand(5, 10)); if (rand(0, 1)) { return encryptAES128CBC("{$pad1}{$data}{$pad2}", $key, getRandomBytes(16)); } return encryptAES128ECB("{$pad1}{$data}{$pad2}", $key); } // don't output if we're included into another script. if (!debug_backtrace()) { require_once '../01-basics/08-detect-aes-in-ecb-mode.php'; // so the trick is to feed the black box something that will trigger ECBs weakness regardless of padding // this means we need at least 3 blocks of repeated data (with padding this reduces to 2 blocks) $plaintext = str_repeat('a', 48); print "Running 5000 samples\n"; $ecb = 0; for ($i = 0; $i < 5000; $i++) { $ciphertext = randomlyEncryptECBorCBC($plaintext); if (repeatedBlockCount($ciphertext)) { $ecb++; } } print "{$ecb} samples detected as ECB mode\n"; print $ecb && round(5000 / $ecb) === 2.0 ? "Success!\n\n" : "Failure :(\n\n"; }