public void Challenge3_SingleByteXorCipher()
        {
            const string cipherHex  = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736";
            var          cipherData = Utils.HexToByteArray(cipherHex);

            double error;
            var    plainText = SingleByteXorCracker.Crack(cipherData, out error);

            const string expected = "Cooking MC's like a pound of bacon";

            Assert.AreEqual(expected, plainText);
        }
        public void Challenge6_BreakRepeatingKeyXor()
        {
            var data = Utils.GetResourceBase64("RepeatingKeyXor.txt");

            var keySizes = Enumerable.Range(2, 40);

            // this is slightly different than the approach described in the challenge.  We
            // convolve the data against itself and look for the lowest "energy" step.  This
            // is likely where the repeating keys overlap and the plaintext pattern emerges.
            var convolvedKeySizes = keySizes.ToDictionary(keySize => keySize, keySize =>
            {
                var padding = Enumerable.Repeat((byte)0, keySize).ToArray();
                return(Utils.Hamming(padding.Concat(data), data.Concat(padding)));
            }).OrderBy(pair => pair.Value);

            var topKeySizes = convolvedKeySizes.Select(pair => pair.Key).Take(1);

            // distribute data into bins that align with first key byte, second key byte, etc.
            var transposedBlocks =
                topKeySizes.Select(
                    keySize =>
                    Enumerable.Range(0, keySize)
                    .Select(keyOffset => data.Where((x, i) => i % keySize == keyOffset).ToArray()));

            var keySizeKeyErrors = transposedBlocks.Select(pair => pair.Select(k =>
            {
                byte key;
                double error;
                var result = SingleByteXorCracker.Crack(k, out error, out key);
                return(new { Result = result, Key = key, Error = error });
            }));

            var possibleKeys = keySizeKeyErrors.Select(k => k.Select(r => r.Key).ToArray());

            var plaintexts = possibleKeys.ToDictionary(key => key, key =>
            {
                var transform = new XorCryptoTransform(key);

                var stream = new MemoryStream(data);
                using (var cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Read))
                {
                    using (var reader = new StreamReader(cryptoStream))
                        return(reader.ReadToEnd());
                }
            });

            var actual = plaintexts.First().Value;

            var expected = Utils.GetResourceText("Set1PlainText.txt");

            Assert.AreEqual(expected, actual);
        }
        public void Challenge4_DetectSingleCharacterXor()
        {
            var lines    = Utils.GetResourceLines("DetectSingleCharacterXor.txt");
            var lineData = lines.Select(Utils.HexToByteArray);
            var errors   = lineData.Select(line =>
            {
                double error;
                var result = SingleByteXorCracker.Crack(line, out error);
                return(new { Result = result, Error = error });
            }).OrderBy(line => line.Error);

            var best = errors.First();

            const string expected = "Now that the party is jumping\n";

            Assert.AreEqual(expected, best.Result);
        }