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 Challenge5Prep_RoundtripXor()
        {
            const string plaintext = "We have nothing to fear but fear itself";

            var transform = new XorCryptoTransform(Encoding.ASCII.GetBytes("the key!"));

            var stream = new MemoryStream();
            using (var writerStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
            using (var writer = new StreamWriter(writerStream, Encoding.ASCII, 4096, true))
                writer.Write(plaintext);

            var cipherStream = new MemoryStream(stream.ToArray());

            using (var readerStream = new CryptoStream(cipherStream, transform, CryptoStreamMode.Read))
            using (var reader = new StreamReader(readerStream))
                Assert.AreEqual(plaintext, reader.ReadToEnd());
        }
        public void Challenge5Prep_RoundtripXor()
        {
            const string plaintext = "We have nothing to fear but fear itself";

            var transform = new XorCryptoTransform(Encoding.ASCII.GetBytes("the key!"));

            var stream = new MemoryStream();

            using (var writerStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
                using (var writer = new StreamWriter(writerStream, Encoding.ASCII, 4096, true))
                    writer.Write(plaintext);

            var cipherStream = new MemoryStream(stream.ToArray());

            using (var readerStream = new CryptoStream(cipherStream, transform, CryptoStreamMode.Read))
                using (var reader = new StreamReader(readerStream))
                    Assert.AreEqual(plaintext, reader.ReadToEnd());
        }
        public void Challenge5_RepeatingKeyXor()
        {
            var key = Encoding.ASCII.GetBytes("ICE");

            const string plaintext = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal";

            var transform = new XorCryptoTransform(key);

            var stream = new MemoryStream();

            using (var cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(cryptoStream))
                    writer.Write(plaintext);
            }

            var expected =
                Utils.HexToByteArray(
                    "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f");

            var actual = stream.ToArray();

            Assert.IsTrue(expected.SequenceEqual(actual));
        }
        public void Challenge5_RepeatingKeyXor()
        {
            var key = Encoding.ASCII.GetBytes("ICE");

            const string plaintext = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal";

            var transform = new XorCryptoTransform(key);

            var stream = new MemoryStream();
            using (var cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
            {
                using (var writer = new StreamWriter(cryptoStream))
                    writer.Write(plaintext);
            }
            
            var expected =
                Utils.HexToByteArray(
                    "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f");

            var actual = stream.ToArray();

            Assert.IsTrue(expected.SequenceEqual(actual));
        }
        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);
        }