using System; using System.IO; using System.Text; using System.Collections.Generic; namespace AniNIX.Crypto { public class Substitution : Cipher { public char[] EngCommon = {'e','t','a','o','i','n','s','h','r','d','l','u','c','m','w','f','y','g','p','b','v','k','x','j','q','z'}; public override String Description() { return "Substitution ciphers replace characters one-for-one.\nKey format is \"E[EEE]=d[ddd]\", where E is the character in the cipher and d is the intended character in the workspace.\n\nSubstitution can take multiple keys in a single invocation.\n\nExample usage:\nsub decrypt I=j -- replace each I in the input with j in the workspace.\nsub decrypt IN=jq -- replace each I in the input with j in the workspace, and each N in the input with q.\nsub decrypt IN=jq K=c -- do the above, and additionally replace each K with c.\n"; } public override String Command() { return "sub"; } public Substitution(Workbench w) : base (w) {} /// /// We should be able to act on a workspace and command line. Most ciphers will sue the same syntax. Those that don't can override. /// /// The current version of the text being worked on. /// The command sequence. /// The updated version of the workSpace public override String RunCommand(String workSpace,String inputText,String[] line) { if (workSpace == null || line == null || line.Length < 2) { Console.Error.WriteLine("Malformed request."); return workSpace; } switch (line[1]) { case "encrypt": return Encrypt(workSpace,inputText,line); case "decrypt": return Decrypt(workSpace,inputText,line); case "try-common": return TryCommon(inputText); case "help": case "": GetHelp(); return workSpace; default: Console.Error.WriteLine("Invalid command. Type help for more."); return workSpace; } } /// /// Show the helptext for this cipher. By default, most ciphers will only have encrypt, decrypt, and help functions. /// /// This is the incoming line and we use it to get the cipher name public override void GetHelp() { Console.WriteLine(String.Format("Help for the {0} cipher suite.\n{1}\n",Command(),Description())); Console.WriteLine("Usage:\nsub encrypt key[s] -- encrypt with the key[s]\nsub decrypt key[s] -- decrypt with the key[s]\ntry-common -- try common sub keys\nhelp -- show this helptext."); } /// /// Encrypt a string with the cipher, replacing one character with another. /// /// The working copy of the cipher /// The original cipher /// The user input /// The encrypted string public override String Encrypt(String workSpace, String cipher, String[] line) { if (line.Length < 3) { Console.Error.WriteLine("Bad formatting."); return workSpace; } char[] changed = workSpace.ToCharArray(); for (int i=2; i /// Perform a frequency analysis and see if ETAOIN substitution will solve the problem. TODO this should look at a dictionary for confirmation. /// /// the string to analyze and brute-force /// an attempted solution. public String TryCommon(String workSpace) { // Get the frequency analysis List sortedChars = Analysis.GetMostCommonLetters(workSpace.ToLower()); // Strip to lower for now and replace. TODO ensure all manipulation is done in lower case and restored later. char[] modified = workSpace.ToLower().ToCharArray(); char replaceChar; // For each character in the string, replace with its frequency map equivalent. for (int i = 0; i < modified.Length; i++) { if (!Char.IsLetter(modified[i])) { continue; } Console.WriteLine(String.Format("Character <{0}> occurs {1}st in frequency, corresponding with <{2}> -- replacing...", modified[i], sortedChars.IndexOf(modified[i].ToString()), EngCommon[sortedChars.IndexOf(modified[i].ToString())])); replaceChar = EngCommon[sortedChars.IndexOf(modified[i].ToString())]; replaceChar = (workSpace[i] == Char.ToLower(workSpace[i])) ? replaceChar : Char.ToUpper(replaceChar); modified[i] = replaceChar; } return new String(modified); } } }