using System; using System.Collections.Generic; using System.Linq; using System.Text; using ExtensionMethods; namespace CryptanalysisCore { public static class Analyse { public enum TextTypes { WithSpacesLower, WithSpacesUpper, WithoutSpacesLower, WithoutSpacesUpper } public const int OneGram = 1; public const int Bigram = 2; public const int Trigram = 3; public const char Unknown = '?'; /// /// Počet písmen v abecedě /// public const int AlphabetLetterCount = 26; /// /// Zjistí, jak moc daný text odpovídá zvyklostem daného jazyka. /// /// Jakýkoliv text daného jazyka /// Vlastnosti a charakteristiky jazyka /// Procentuální vyjádření shody s vlastnostmi jazyka public static double SimilarityIndex(string text, LangCharacteristic lang) { double similarity = 0; similarity += 3 * SumSimilarity(text, lang.Letters); similarity += SumSimilarity(text, lang.Bigrams); similarity += SumSimilarity(text, lang.Trigrams); return similarity; } /// /// Vrátí součet rozdílů očekávaných a nalezených procentuálních výskytů znaků /// /// /// /// private static double SumSimilarity(string text, Dictionary occurrence) { Dictionary standardLettersOccurrence = occurrence.Take(6).ToDictionary(x => x.Key, x => x.Value); Dictionary inTextLettersOcc = TextAnalysis.GetOccurrence(text, occurrence.Keys.Take(1).ToArray()[0].Length); double sum = 0; foreach (KeyValuePair occ in standardLettersOccurrence) { if (inTextLettersOcc.ContainsKey(occ.Key)) { sum += Math.Abs(occ.Value - inTextLettersOcc[occ.Key]); } else { sum += 10; } } return sum; } /// /// Vrací vzdálenost dvou písmen /// /// /// /// Vzdálenost dvou písmen public static int Distance(char a, char b) { return Math.Abs((int)a - (int)b); } /// /// Vrací minimální vzdálenost, to jest minimální počet /// záporných nebo kladných posunů z písmene a do b. /// /// /// /// Minimální vzdálenost dvou písmen public static int MinDistance(char a, char b) { int diff = Math.Abs((int)a - (int)b); return diff < (double)AlphabetLetterCount / 2 ? diff : AlphabetLetterCount - diff; } /// /// Převede text do formy vhodné k šifrování a dešifrování /// Odstraní diakritiku, převede text na malá/velká písmena, /// odstraní všechna nepísmena včetně/mimo mezer. /// /// Text, který chceme normalizovat /// Typ normalizace /// Normalizovaný řetězec public static string NormalizeText(string text, TextTypes textType) { string normText = text.RemoveDiacritics().Replace(new string[] { ".", ",", "!", "?", "\n", "\r", ";", " " }, " "); switch (textType) { case TextTypes.WithoutSpacesLower: return normText.Filter(c => TextAnalysis.IsEnglishLetter(c)).ToLower(); case TextTypes.WithoutSpacesUpper: return normText.Filter(c => TextAnalysis.IsEnglishLetter(c)).ToUpper(); case TextTypes.WithSpacesLower: return TextAnalysis.GetLetters(normText).ToLower(); case TextTypes.WithSpacesUpper: return TextAnalysis.GetLetters(normText).ToUpper(); default: throw new NotImplementedException(); } } /// /// Předanou metodu spustí se všemi písmeny abecedy /// /// Metoda, která se bude aplikovat na písmena public static void DoAlphabet(Action action) { for (int i = 0; i < AlphabetLetterCount; i++) { action((char)(i + 'a')); } } /// /// Zamění písmena v otevřeném textu za jejich ekvivalenty /// /// Text, ve kterém budou provedeny záměny /// Definice záměn /// Nový řetězec se změněnými znaky public static string SwitchLetters(string opentext, Dictionary lettersSubstitution) { opentext = opentext.ToLower(); char[] ciphertext = new char[opentext.Length]; for (int i = 0; i < opentext.Length; i++) if (lettersSubstitution.ContainsKey(opentext[i])) ciphertext[i] = lettersSubstitution[opentext[i]]; else ciphertext[i] = opentext[i]; return new string(ciphertext); } /// /// Vrátí relativní (procentuální) výskyt znaků v řetězci. /// /// Libovolný řetězec /// Predikát, kterým budou testovány jednotlivé znaky /// Slovník ve tvaru znak - počet výskytů vzhledem k celkovému počtu znaků public static Dictionary CharsRelativeOccurrence(String s, Predicate predicate) { Dictionary occurrences = CharsOccurrence(s, predicate); Dictionary relativeOccurences = new Dictionary(); foreach (KeyValuePair occurrence in occurrences) { relativeOccurences[occurrence.Key] = (double)occurrence.Value / (double)s.Length * 100; } DoAlphabet(letter => { if (!relativeOccurences.ContainsKey(letter)) relativeOccurences[letter] = 0; }); relativeOccurences = relativeOccurences.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, y => y.Value); return relativeOccurences; } /// /// Vrátí relativní (procentuální) výskyt znaků v řetězci. /// /// Libovolný řetězec /// Slovník ve tvaru znak - počet výskytů vzhledem k celkovému počtu znaků public static Dictionary CharsRelativeOccurrence(String s) { return CharsRelativeOccurrence(s, x => true); } /// /// Zjistí počet jednotlivých znaků v řetězci /// /// Libovolný řetězec /// Slovník ve tvaru znak - počet výskytů public static Dictionary CharsOccurrence(String s) { return CharsOccurrence(s, (x) => true); } /// /// Zjistí počet jednotlivých znaků v řetězci, které odpovídají předanému predikátu /// /// Libovolný řetězec /// Predikát, kterým budou testovány jednotlivé znaky /// Slovník ve tvaru znak - počet výskytů public static Dictionary CharsOccurrence(String s, Predicate predicate) { Dictionary occurrence = new Dictionary(); foreach (char character in s) { if (occurrence.ContainsKey(character)) { occurrence[character]++; } else { if (predicate(character)) { occurrence[character] = 1; } } } return occurrence; } /// /// Posune předaný znak v abecedě podle parametru 'IndikátorPosunu'. /// Pokud bude například Znak = 'e' a IndikátorPosunu = 'c', bude písmeno /// 'e' posunuto o dvě pozice na 'g'. /// /// Znak, který má být posunut. /// Znak který určuje jak dlouhé má být posunutí. /// Posunutý znak. public static char MoveCharacter(char character, char offset) { if (char.IsLower(character) && char.IsUpper(offset) || char.IsUpper(character) && char.IsLower(offset)) throw new ArgumentException("Písmena musí být buď obě velká nebo obě malá"); // Sečteme ASCI hodnoty obou znaků int MovedCharacter = (int)character + (int)offset; MovedCharacter -= char.IsLower(character) ? (int)'a' : (int)'A'; // Jestli jsme se dostali za konec abecedy, začneme počítat od začátku abecedy if ((char.IsLower(character) && MovedCharacter > (int)'z') || (char.IsUpper(character) && MovedCharacter > (int)'Z')) MovedCharacter -= 26; // Převedeme číslo zpět na char a vrátíme return (char)MovedCharacter; } public static char MoveBackCharacter(char character, char offset) { if (offset == Analyse.Unknown) return Analyse.Unknown; int MovedCharacter = (int)character - (int)offset; MovedCharacter += char.IsLower(character) ? (int)'a' : (int)'A'; if ((char.IsLower(character) && MovedCharacter < (int)'a') || (char.IsUpper(character) && MovedCharacter < (int)'A')) MovedCharacter += 26; return (char)MovedCharacter; } /// /// Zjistí, zda záměny písmen definované v letters odpovídají /// transformaci openWord->cipherWord /// /// /// /// /// public static bool MatchLetters(string openWord, string cipherWord, Dictionary letters) { for (int i = 0; i < openWord.Length; i++) { if (letters.ContainsKey(openWord[i])) { if (letters[openWord[i]] != cipherWord[i]) return false; } } return true; } /// /// Zjistí, kolik slov se nachází v textu /// /// /// /// public static int WordsContains(string text, string[] words) { int counter = 0; foreach (string word in words) if (text.Contains(word)) counter++; return counter; } } }