require_once '../01-basics/06-break-repeating-key-xor.php'; require_once '18-implement-ctr-the-stream-cipher-mode.php'; $plaintexts = array_map('base64_decode', file('20-data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); $key = getRandomBytes(16); $ciphertexts = array_map('encryptAES128CTR', $plaintexts, array_fill(0, count($plaintexts), $key)); $cipherLens = array_map('strlen', $ciphertexts); // challenge text says use a common length, but we can recover more if we don't // this is because after transposition there's still enough data to statistically recover more /* $minLength = min($cipherLens); $truncated = array_map('str_split', $ciphertexts, array_fill(0, count($plaintexts), $minLength)); $truncated = array_column($truncated, 0); */ $truncated = $ciphertexts; // some copy/paste/tweak from challenge 6 print "\nSolving keys based on English Language scoring:\n"; $blocks = transposeBlocks($ciphertexts); $englishLanguageWeights['/'] = 0; list($topScores, $topChars) = scoreSingleByteXORStrings($blocks, $englishLanguageWeights, 20); $potentialKey = implode(array_map('chr', $topChars)); foreach ($truncated as $k => $ciphertext) { $recovered = $ciphertext ^ $potentialKey; print "{$k}: {$recovered}\n"; } print "\n\nSome texts will not be fully recovered.\nThis is expected for simple automatic statistical recovery.\n\n\n"; foreach ($truncated as $k => $ciphertext) { $recovered = $ciphertext ^ $potentialKey; if ($recovered !== $plaintexts[$k]) { print "Cracked : {$recovered}\nOriginal: {$plaintexts[$k]}\n\n"; } }
$scores = scoreHammedKeySizeRange($data, 2, 40, 10000); // 10k sample limit to reduce time taken print "Top scoring key sizes:\n"; $i = 0; foreach ($scores as $k => $v) { print "{$k} - {$v}\n"; if (++$i === 3) { break; } } $potentialKeys = []; print "\nSolving keys based on English Language scoring:\n"; $i = 0; foreach ($scores as $k => $v) { $blocks = str_split($data, $k); $blocks = transposeBlocks($blocks); list($topScores, $topChars) = scoreSingleByteXORStrings($blocks, $englishLanguageWeights); $potentialKeys[$k] = implode(array_map('chr', $topChars)); print "{$k}: {$potentialKeys[$k]}\n"; if (++$i === 3) { break; } } print "\nDecrypted data with solved keys:\n"; $i = 0; foreach ($scores as $k => $v) { $decrypted = $data ^ str_repeat($potentialKeys[$k], ceil($dataLen / strlen($potentialKeys[$k]))); print "{$k}: {$decrypted}\n\n\n"; if (++$i === 3) { break; }