/// <summary> /// Returns the best key using the caesar cipher /// </summary> /// <param name="str">Ciphertext.</param> /// <returns></returns> public static Dictionary <char, char> CheckCaesar(String str) { Quadgram_Analysis analysis = new Quadgram_Analysis(); Dictionary <char, char> key = null; double bestFitness = -99999; String alphabet = "abcdefghijklmnopqrstuvwxyz"; for (int amt = 1; amt < 26; amt++) { //shift each character of the string "amt" number of letters String shiftedString = Shift(str, amt); //test the fitness of this shifted string double curFitness = analysis.TestKeyFitness(GetQuadgrams(shiftedString)); if (curFitness > bestFitness) { //builds a key from this shift if it's the best we've seen key = new Dictionary <char, char>(); //we know the key for the shift, so we're going to go trhough the alphabet and //shift each character the correct amount to build a key for (int i = 0; i < 26; i++) { //a bit of a hack, but to utilize the shift() method on characters, //converts the char to a string, and converts it back to a char array //since it's only a character, we know the answer is in index 0 char[] shiftedChar = Shift(alphabet[i].ToString(), amt).ToCharArray(); key.Add(alphabet[i], shiftedChar[0]); } bestFitness = curFitness; } } return(key); }
public static String Decipher(String encStr) { Quadgram_Analysis analysis = new Quadgram_Analysis(); Dictionary <char, char> parentKey = GenerateRandomKey(); HashSet <String> testedKeys = new HashSet <String>(); testedKeys.Add(ReadKey(parentKey)); double fitness = analysis.TestKeyFitness(GetQuadgrams(DecipherMessageUsingKey(parentKey, encStr))); String message = encStr.ToString(); double highest = -999999; double absHighest = -99999; int tries = 0; int abs = 0; String thisMessage = null; String retMessage = null; while (abs < 15) { thisMessage = null; for (int i = 0; i < 1000; i++) { Dictionary <char, char> childKey = new Dictionary <char, char>(parentKey); do { SwapTwoLetters(childKey); } while (testedKeys.Contains(ReadKey(childKey))); String currentMessage = DecipherMessageUsingKey(childKey, message); double newFitness = 0; newFitness = analysis.TestKeyFitness(GetQuadgrams(currentMessage)); if (newFitness > fitness) { fitness = newFitness; parentKey = new Dictionary <char, char>(childKey); thisMessage = currentMessage; } testedKeys.Add(ReadKey(childKey)); } //Console.WriteLine(fitness); if (fitness > highest) { tries = 0; highest = fitness; retMessage = thisMessage; Console.WriteLine(thisMessage + " " + fitness); if (fitness > absHighest) { absHighest = fitness; abs++; Console.WriteLine(abs); } } else if (fitness == highest || fitness < highest) { tries++; //Console.WriteLine(++tries); do { parentKey = GenerateRandomKey(); } while (testedKeys.Contains(ReadKey(parentKey))); fitness = analysis.TestKeyFitness(GetQuadgrams(DecipherMessageUsingKey(parentKey, encStr))); //stuck at local max if (tries > 100) { Console.WriteLine("hit local max"); highest = -99999; } } //tries++; } return(retMessage); }
/// <summary> /// Deciphers ciphertext using a hill climbing algorithm. /// /// Algorithm orginally explained at /// http://practicalcryptography.com/cryptanalysis/stochastic-searching/cryptanalysis-simple-substitution-cipher/ /// /// Has a difference that rather than randomly switching letters, it switches letters iteratively. /// Read somewhere that was slightly better, also gives a clearer point where to stop. /// </summary> /// <param name="encStr"></param> /// <returns>Returns a char-char dictionary giving the algorithm's /// best guess for the key. Each key relates to it's corresponding /// ciphertext character. </returns> public static Dictionary <char, char> Decipher(String encStr) { HashSet <String> testedKeys = new HashSet <string>(); Quadgram_Analysis analysis = new Quadgram_Analysis(); Dictionary <char, char> retKey = null; //we're going to get the best ceasar key and fitness for future use Dictionary <char, char> caesarKey = CheckCaesar(encStr); double caesarFitness = analysis.TestKeyFitness(GetQuadgrams(DecipherMessageUsingKey(caesarKey, encStr))); //best fitness we've encountered double bestFitness = -99999; //number of times we've encountered the best fitness int bestFitnesses = 0; for (int k = 0; k < 1000; k++) { //generate a random key and check its fitness Dictionary <char, char> key = GenerateRandomKey(); double fitness = analysis.TestKeyFitness(GetQuadgrams(DecipherMessageUsingKey(key, encStr))); //determines if we've found a better key for this key bool betterKeyFound; do { betterKeyFound = false; /* * We're going to loop through the key and test all possible "shuffles" of this key, * in which we switch each letter with every other letter and see what the fitness is. * If the fitness is better, then we will go with that key and continue switching letters. */ for (int i = 0; i < 25; i++) { for (int j = 1; j < 26; j++) { //switch letters KeyValuePair <char, char> temp = key.ElementAt(i); KeyValuePair <char, char> temp1 = key.ElementAt(j); key[temp.Key] = temp1.Value; key[temp1.Key] = temp.Value; //get the fitness of this new message double newFitness = analysis.TestKeyFitness(GetQuadgrams(DecipherMessageUsingKey(key, encStr))); if (newFitness > fitness) { betterKeyFound = true; fitness = newFitness; if (fitness > bestFitness) { bestFitness = fitness; bestFitnesses = 1; retKey = key; } else if (fitness == bestFitness) { /*if we've hit the same best fitness 3 times and it's * better than any caesar fitness we've seen, we'll assume that's * probably the best guess, and we'll end the algorithm. */ if (++bestFitnesses == 3 && bestFitness > caesarFitness) { return(key); } } } else { /* * Otherwise, we did not get a better key with this switch, so let's * revert the changes back. */ key[temp.Key] = temp.Value; key[temp1.Key] = temp1.Value; } } } /* * If it ISN'T a caesar cipher, the caesar fitness should be terrible * and we should've found a better fitness through this algorithm * by now. Thus, if we've tried 650 keys and NONE of them are better than * the caesar cipher....it's probably a caesar cipher. */ if ((bestFitness < caesarFitness)) { return(caesarKey); } } while (betterKeyFound); } return(retKey); }