/// <summary> /// Same as FindKeySize, but uses the avg distance between the first and each subsequent block instead of just the first two blocks. This results /// in a more fully evaluated result, which is more likely to be correct. /// /// USAGE: Doesn't actually work. Despite the challenge author's claims, this doesn't work consistently. I'm leaving it as-is for now and we'll /// see if we need to come back to it later. See more notes on _CopyFromInternet version. /// </summary> /// <param name="encryptedData"></param> /// <returns></returns> public static int FindKeySize_AvgDistance(byte[] encryptedData) { EditDistanceComputer edc = new EditDistanceComputer(); int lowestKeySize = 100000000; float lowestNormalizedDistance = 100000000.0f; //debug StringBuilder debug = new StringBuilder(); //Try various key size guesses. for (int keySize = 2; keySize <= 40; keySize++) { //A bit of an assumption here... but we're // attempting a repeating XOR cipher. If the key is, say, 10 bytes long, but the text is only 18 bytes long, // then we haven't really used a repeating XOR cipher - it finished before we repeated. So let's terminate any keys // if we reach half the distance of the encrypted data. if (encryptedData.Length < keySize * 2) { break; } //Get the first keySize bytes out of the encrypted data byte[] p1 = GetNBytes(encryptedData, 0, keySize); float totalNormalizedDistance = 0.0f; //Make an array of all remaining blocks possible int numAddtlBlocks = (encryptedData.Length - keySize) / 2; for (int i = 1; i <= numAddtlBlocks; i++) { byte[] compare = GetNBytes(encryptedData, keySize * i, keySize); //Find the distance between those 2 values. int distance = edc.ComputeBitDistanceBetweenByteArrays(p1, compare); //Normalize the value by the length so the shortest key isn't automatically the winner totalNormalizedDistance += ((float)distance / (float)keySize); } float avgNormalizedDistance = totalNormalizedDistance / numAddtlBlocks; if (avgNormalizedDistance < lowestNormalizedDistance) { lowestNormalizedDistance = avgNormalizedDistance; lowestKeySize = keySize; } debug.AppendFormat("Length: {0}\tDistance: {1}\tNormalized:{2}\r\n", keySize, totalNormalizedDistance, avgNormalizedDistance); } string s = debug.ToString(); return(lowestKeySize); }
/// <summary> /// Like _AvgDistance, except instead of comparing block 0 to each remaining block, we just compare neighboring blocks /// /// USAGE: Doesn't actually work. Despite the challenge author's claims, this doesn't work consistently. I'm leaving it as-is for now and we'll /// see if we need to come back to it later. See more notes on _CopyFromInternet version. /// </summary> /// <param name="encryptedData"></param> /// <returns></returns> public static int FindKeySize_AvgDistanceByNeighbor(byte[] encryptedData) { EditDistanceComputer edc = new EditDistanceComputer(); int lowestKeySize = 100000000; float lowestNormalizedDistance = 100000000.0f; //debug StringBuilder debug = new StringBuilder(); //Try various key size guesses. for (int keySize = 2; keySize <= 40; keySize++) { //A bit of an assumption here... but we're // attempting a repeating XOR cipher. If the key is, say, 10 bytes long, but the text is only 18 bytes long, // then we haven't really used a repeating XOR cipher - it finished before we repeated. So let's terminate any keys // if we reach half the distance of the encrypted data. if (encryptedData.Length < keySize * 2) { break; } float totalNormalizedDistance = 0.0f; int numBlocks = (int)Math.Ceiling(encryptedData.Length / (double)keySize); for (int i = 0; i < encryptedData.Length; i += keySize) { byte[] b1 = GetNBytes(encryptedData, keySize * i, keySize); byte[] b2 = GetNBytes(encryptedData, keySize * (i + 1), keySize); //Find the distance between those 2 values. int distance = edc.ComputeBitDistanceBetweenByteArrays(b1, b2); //Normalize the value by the length so the shortest key isn't automatically the winner //TODO: totalNormalizedDistance += ((float)distance / (float)keySize); totalNormalizedDistance += (float)distance; } float avgNormalizedDistance = totalNormalizedDistance / numBlocks / keySize; if (avgNormalizedDistance < lowestNormalizedDistance) { lowestNormalizedDistance = avgNormalizedDistance; lowestKeySize = keySize; } debug.AppendFormat("Length: {0}\tDistance: {1}\tNormalized:{2}\r\n", keySize, totalNormalizedDistance, avgNormalizedDistance); } string s = debug.ToString(); return(lowestKeySize); }
/// <summary> /// Given some encrypted data, attempts to find the key size by comparing the edit distance between blocks of various size. /// /// USAGE: Don't use this one. This is the most simplistic version, comparing only the first 2 blocks. Despite a suggestion /// from the challenge authors to do this, it doesn't actually succeed. /// </summary> /// <param name="encryptedData">The encrypted data to be analyzed</param> /// <returns>The length in bytes of the key that likely encrypted that data</returns> public static int FindKeySize_DoNotUse(byte[] encryptedData) { EditDistanceComputer edc = new EditDistanceComputer(); int lowestKeySize = 100000000; float lowestNormalizedDistance = 100000000.0f; //debug StringBuilder debug = new StringBuilder(); //Try various key size guesses. for (int keySize = 2; keySize <= 37; keySize++) { //If the string isn't long enough, don't try any further key sizes if (encryptedData.Length < keySize * 2) { break; } //Get the first keySize bytes out of the encrypted data byte[] p1 = GetNBytes(encryptedData, 0, keySize); //Get the second keySize bytes out of the encrypted data byte[] p2 = GetNBytes(encryptedData, keySize, keySize); //Find the distance between those 2 values. int distance2 = edc.ComputeBitDistanceBetweenByteArrays(p1, p2); //Normalize the value by the length so the shortest key isn't automatically the winner //TODO: Is this good enough? Comments suggest we might want to be more particular. float normalized = ((float)distance2 / (float)keySize); if (normalized < lowestNormalizedDistance) { lowestNormalizedDistance = normalized; lowestKeySize = keySize; } debug.AppendFormat("Length: {0}\tDistance: {1}\tNormalized:{2}\r\n", keySize, distance2, normalized); } string s = debug.ToString(); return(lowestKeySize); }
/// <summary> /// https://www.scottbrady91.com/Cryptopals/Caesar-and-Vigenere-Ciphers /// /// This should be the same algorithm I have above... but it gets different results, so I've done something wrong... /// /// However, it *only* works for the given cryptopals example. Any other test I give it fails to return the right key length. /// /// USAGE: This works to break the specific Challenge06 cypher. It works for that phrase & key and returns the proper value. /// However, this is not a useful function. It seems like they cherry picked something that happens to work. I tried this with /// several other phrases/keys and none of them work right. I also found several others online implementing this and they /// have the same problem - it doesn't break anything except rare cyphers (one of which just happens to be the challenge). /// Here's an online example with I believe similar code you can play with to see the faults: /// https://thmsdnnr.com/cryptopals/s1c6/index.html /// /// Given the time investment so far, I'm going to leave this as-is and see how we go. /// </summary> /// <param name="encryptedData"></param> /// <returns></returns> public static int FindKeySize_CopyFromInternet(byte[] encryptedData) { //debug StringBuilder debug = new StringBuilder(); // EditDistanceComputer edc = new EditDistanceComputer(); var keySizeResults = new Dictionary <int, double>(); // 1. "Let KEYSIZE be the guessed length of the key; try values from 2 to (say) 40." for (var keySize = 2; keySize <= 40; keySize++) { // 2. For hamming distance tests see Funcationlity\StringExtensionTests.cs // 3. "For each KEYSIZE, take the first KEYSIZE worth of bytes, // and the second KEYSIZE worth of bytes, and find the edit distance between them. // Normalize this result by dividing by KEYSIZE." var hammingDistance = 0; var numberOfHams = 0; for (int i = 1; i < encryptedData.Length / keySize; i++) { var firstKeySizeBytes = encryptedData.Skip(keySize * (i - 1)).Take(keySize); var secondKeySizeBytes = encryptedData.Skip(keySize * i).Take(keySize); hammingDistance += edc.ComputeBitDistanceBetweenByteArrays(firstKeySizeBytes.ToArray(), secondKeySizeBytes.ToArray()); numberOfHams++; } if (numberOfHams > 0) { double normalizedDistance = (double)hammingDistance / numberOfHams / keySize; keySizeResults.Add(keySize, normalizedDistance); debug.AppendFormat("Length: {0}\tNormalized:{1}\r\n", keySize, normalizedDistance); } } string s = debug.ToString(); return(keySizeResults.Aggregate((l, r) => l.Value < r.Value ? l : r).Key); }