using System; using System.IO; using System.Linq; using System.Text; using System.Collections.Generic; using System.Reflection; using AniNIX.Shared; namespace AniNIX.Crypto { public class Workbench { public string inputText { get; private set; } public string workSpace { get; private set; } public StringBuilder HelpText = new StringBuilder(); public Dictionary SwitchCases = new Dictionary(); // The workbench needs to maintain an instance of each ciphersuite for operation. private List _ciphers = new List(); // If this is true, we will prevent prompt and filesystem access. private bool _isBlind = false; /// /// Read in a cipher either from a filename or from stdin /// /// user input private void ReadCipher(String[] line) { // If a filename's not provided. if (line == null || line.Length !=2) { Console.WriteLine("Please paste your ciphertext. End your input with a trailing newline."); string readLn = Console.ReadLine(); StringBuilder sb = new StringBuilder(); //Read lines from stdin until a blank line is given. while (readLn != null && !String.IsNullOrWhiteSpace((readLn))) { sb.AppendLine(readLn); readLn=Console.ReadLine(); } // The user's input is the trimmed sum of the lines. inputText = sb.ToString().Trim(); } else { // Try to read the file into the input. try { StringBuilder sb = new StringBuilder(); StreamReader fileReader = new StreamReader(line[1]); String lineR = fileReader.ReadLine(); while (lineR != null) { sb.AppendLine(lineR); lineR = fileReader.ReadLine(); } fileReader.Dispose(); fileReader = null; inputText = sb.ToString().Trim(); Console.WriteLine(String.Format("Read {0}",line[1])); } // If there's a problem, input is null. catch (Exception e) { Console.Error.WriteLine(e.Message); inputText = null; } } workSpace = inputText; } /// /// Create a new workbench from input. /// /// the arguments provided for execution public Workbench(string[] args) { // If args are null, read from STDIN. if (args.Length == 0) { ReadCipher(null); // If there's only one arg and that's --blind, set the blind option and read from stdin. } else if (args.Length == 1) { if (args[0].Equals("--blind")) { this._isBlind = true; ReadCipher(null); // Otherwise, try to use the first argument as a filename. } else if (args[0].Equals("--help") || args[0].Equals("-h")) { } else { String[] line = new String[2]; line[0] = "reread"; line[1] = args[0]; ReadCipher(line); } // Otherwise, give some help and exit. } else { Console.Error.WriteLine("The only argument allowed is a filename containing the ciphertext or --blind to block filesystem access."); System.Environment.Exit(1); } // Seed the helptext. HelpText.Append("Available commands:\n"); // Don't tell users about things they can't use. if (!_isBlind) { HelpText.Append("reread -- Read in a new cipher\n"); HelpText.Append("write -- write the workspace to a file\n"); } HelpText.Append("reset -- reset workspace to the ciphertext.\n"); HelpText.Append("regex -- Check for strings with the two regex arguments: [search] [filter]\n"); HelpText.Append("links -- show some helpful links\n"); HelpText.Append("print -- show the current workspace\n"); HelpText.Append("display -- alias of print\n"); // Initialize the ciphersuites. Object[] cipherArgs = { (Object)this }; foreach (Type cipherType in Assembly.GetAssembly(typeof(Cipher)).GetTypes() .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(Cipher)))) { _ciphers.Add((Cipher)Activator.CreateInstance(cipherType, cipherArgs)); } HelpText.Append("exit -- exit and show the result.\n"); HelpText.Append("quit -- alias of exit.\n"); HelpText.Append("help -- show this HelpText\n"); HelpText.Append("\nYou can get help on any command by running \" help\".\nSuppress printing the cipher with a trailing ;.\n\nCommand structure is: {module} {operation} [{keypart}]*\n\nExample commands:\n\"caesar encrypt 13\" -- Encrypt the input with a Caesarian cipher of offset 13.\n\"affine help\" -- Get help on the Affine cipher.\n\"simple stripspace\" -- Use the Simple module to remove spaces.\n\"analysis one-to-one\" -- Check if the workspace is a one-to-one correlation with the input.\n\n"); } /// /// Convert this workbench into a readable string. /// public override String ToString() { StringBuilder currentStatus = new StringBuilder(); currentStatus.Append("Input:\n"); currentStatus.Append(this.inputText); currentStatus.Append("\n"); currentStatus.Append("Workspace:\n"); currentStatus.Append(this.workSpace); return currentStatus.ToString(); } /// /// Display this workbench to stdout with colors. /// // Set to true if you want verbose prints. public void Print() { Print(false); } public void Print(bool verbose) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Input:"); Console.ResetColor(); Console.WriteLine(this.inputText); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Workspace:"); Console.ResetColor(); if (verbose) return; List topletters = Analysis.GetMostCommonLetters(workSpace).Take(5).ToList();//cyan List bigrams = Analysis.Top(Analysis.GetSubstrings(workSpace,2)); //yellow List trigrams = Analysis.Top(Analysis.GetSubstrings(workSpace,3));//magenta for (int i = 0; i < workSpace.Length; i++) { if (i < workSpace.Length-1 && workSpace[i] == workSpace[i+1]) { Console.ForegroundColor = ConsoleColor.Green; Console.Write(workSpace[i++]); } else if (i < workSpace.Length-2 && trigrams.Contains(workSpace.Substring(i,3))) { Console.ForegroundColor = ConsoleColor.Magenta; Console.Write(workSpace[i++]); Console.Write(workSpace[i++]); } else if (i < workSpace.Length-1 && bigrams.Contains(workSpace.Substring(i,2))) { Console.ForegroundColor = ConsoleColor.Yellow; Console.Write(workSpace[i++]); } else if (topletters.Contains(workSpace[i].ToString())) { Console.ForegroundColor = ConsoleColor.Cyan; } Console.Write(workSpace[i]); Console.ResetColor(); } Console.WriteLine(); } /// /// Show some helpful links. /// public static void HelpfulLinks() { StringBuilder linksText = new StringBuilder(); linksText.Append("http://www.visca.com/regexdict/ -- RegEx word dictionary\n"); linksText.Append("http://rumkin.com/tools/cipher/ -- Cipher tools\n"); linksText.Append("http://norvig.com/mayzner.html -- Frequency analysis\n"); Console.Write(linksText.ToString()); } /// /// Save the workspace to a file /// /// what to write /// user input, which should include a file public void WriteWorkspace(String workSpace, String[] line) { // Require a filename. if (line == null || line.Length != 2) { Console.Error.WriteLine("Need a file."); return; } try { // If we are actually given a file, write the workspace to a file and close. StreamWriter fileWriter = new StreamWriter(line[1],false); fileWriter.WriteLine(workSpace); fileWriter.Dispose(); fileWriter = null; Console.WriteLine(String.Format("Wrote file {0}",line[1])); } catch (Exception e) { // Let the user know the file's not writeable. Console.WriteLine(String.Format("Couldn't write file.\n{0}",e.Message)); } } /// /// Interact with the user. /// public void Run() { // Display the header. Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("### Welcome to the AniNIX/CryptoWorkbench ###"); Console.ResetColor(); Console.WriteLine("Type help for assistance.\n"); Print(); try { // Set the initial command to be show the helptext. string command = ""; string read = ""; string[] line; bool showCipher=true; // Until the user exits... while (command != "exit" && command != "quit") { // parse shell-type command unification with semicolons foreach (String executable in read.Split(';')) { // If the command is only whitespace, continue and don't show the cipher. if (String.IsNullOrWhiteSpace(executable)) { showCipher = false; // if the last nonwhitespace character is a semicolon, we won't bother printing. continue; } // Otherwise we will show the cipehr. showCipher = true; line = executable.Trim().Split(' '); command = line[0]; // Command is first space-delimited entry. switch (command) { case "reread": if (!_isBlind) ReadCipher(line); break; case "write": if (!_isBlind) WriteWorkspace(workSpace,line); break; case "reset": this.workSpace = this.inputText; Console.Clear(); Console.WriteLine("Reset."); break; case "links": HelpfulLinks(); break; case "regex": try { if (line.Length == 3) { Console.Write(ExecuteCommand.Run(String.Format("bash /opt/aninix/CryptoWorkbench/regex-lookup.bash \"{0}\" \"{1}\"",line[1].Replace("\\","\\\\").Replace("$","\\$"),line[2].Replace("\\","\\\\").Replace("$","\\$")))); } else if (line.Length == 2) { Console.Write(ExecuteCommand.Run(String.Format("bash /opt/aninix/CryptoWorkbench/regex-lookup.bash \"{0}\"",line[1].Replace("\\","\\\\").Replace("$","\\$")))); Console.WriteLine(); } else { Console.Error.WriteLine("Need at least one search term."); } } catch (Exception e) { Console.Error.WriteLine(e.ToString()); } break; case "help": Console.WriteLine(HelpText.ToString()); break; case "display": case "print": showCipher = true; break; case "exit": case "quit": throw new Exception(""); default: try { workSpace = SwitchCases[command].RunCommand(this.workSpace,this.inputText,line); } catch (Exception e) { e.ToString(); Console.Error.WriteLine("Command not found."); } break; } } // Show the cipher if the user asked. if (showCipher) Print(); // Display an AniNIX-standard prompt. Console.Write("\nCW "); Console.ForegroundColor = ConsoleColor.Red; Console.Write("|"); Console.ResetColor(); Console.Write("> "); read = Console.ReadLine().Trim(); } // If we run into trouble, tell the user. } catch (Exception e) { Console.Error.WriteLine(e.Message); } // When we exit, show the result to the user. finally { Console.WriteLine("\nFinal result:"); this.Print(); } } /// /// Create and execute a workbench program. /// public static void Main(string[] args) { Workbench cw = new Workbench(args); try { cw.Run(); } catch (NullReferenceException e) { Console.Error.WriteLine(String.Format("Caught {0}",e.GetType().ToString())); } } } }