diff --git a/.gitignore b/.gitignore index 85cd24f..4f489ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ CryptoApplet.java +cryptoworkbench.exe diff --git a/affine.csharp b/affine.csharp index 9590760..63d88b0 100644 --- a/affine.csharp +++ b/affine.csharp @@ -11,21 +11,33 @@ namespace AniNIX.Crypto { public override String Command() {return "affine";} public Affine(Workbench w) : base (w) {} + /// + /// Encrypt a string with the Affine cipher + /// + /// A parameter for X + /// A parameter for Y + /// The line given by the user + /// The return value public override String Encrypt(String workSpace,String inputText,String[] line) { + // Affine requires two numbers if (line == null || line.Length != 4) { Console.Error.WriteLine("Malformed!"); return workSpace; } char[] changed = workSpace.ToCharArray(); try { + // Convert the first number to an int int a = Int32.Parse(line[2]); + // Validate the first number is coprime to 26. try { MultiplicativeInverse(a); } catch (Exception e) { Console.Error.WriteLine(String.Format("Value a <{0}> is not coprime to 26.\n{1}",a,e.Message)); return workSpace; } + // Convert the second number to an int int b = Int32.Parse(line[3]); + // For each letter in the workSpace, apply the forumula e(x) = (ax + b) mod m for (int i = 0; i < changed.Length; i++) { if (Char.IsLetter(changed[i])) { int baseC = (Char.IsUpper(changed[i])) ? (int)'A' : (int)'a'; @@ -33,6 +45,7 @@ namespace AniNIX.Crypto { changed[i] = (char)(((a*modC+b)%26)+baseC); } } + // If there's a problem solving, tell the user. } catch (Exception e) { Console.Error.WriteLine(String.Format("Failed!\n{0}",e.Message)); return workSpace; @@ -40,8 +53,14 @@ namespace AniNIX.Crypto { return new String(changed); } - public int MultiplicativeInverse(int a) { + /// + /// Find the multiplicative inverse of a number. + /// + /// A number + /// The inverse + public int MultiplicativeInverse(int a) { for (int x=1; x < 27; x++) { + //Try to find a number where the input times that number mod 26 is 1. If we roll through 26 numbers and don't find it, there isn't one. if ((a*x)%26 == 1) { Console.WriteLine(String.Format("Found Multiplicative Inverse of {0}",x)); return x; @@ -50,16 +69,27 @@ namespace AniNIX.Crypto { throw new Exception("A is not coprime."); } + /// + /// Decrypt a string with the cipher + /// + /// A parameter for X + /// A parameter for Y + /// The user input + /// The decrypted string public override String Decrypt(String workSpace,String inputText,String[] line) { + // Decryption requires two numbers. if (line == null || line.Length != 4) { Console.Error.WriteLine("Malformed!"); return workSpace; } char[] changed = workSpace.ToCharArray(); - try { + try { + //Convert both numbers to ints int a = Int32.Parse(line[2]); int b = Int32.Parse(line[3]); + //Find the multiplicative inverse of the first. int multiinv = MultiplicativeInverse(a); + // For each character, decrypt with d(x) = a-1(x - b) mod m for (int i = 0; i < changed.Length; i++) { if (Char.IsLetter(changed[i])) { int baseC = (Char.IsUpper(changed[i])) ? (int)'A' : (int)'a'; diff --git a/analysis.csharp b/analysis.csharp index 030e984..09e0bff 100644 --- a/analysis.csharp +++ b/analysis.csharp @@ -8,11 +8,22 @@ using System.Collections.Generic; namespace AniNIX.Crypto { public class Analysis : Cipher { + private Substitution _sb; public override String Description() { return "Analysis tools"; } public override String Command() { return "analysis"; } - public Analysis(Workbench w) : base (w) {} + public Analysis(Workbench w) : base (w) { + //Analysis needs to be able to read the EngCommon array from the Substition class + this._sb = new Substitution(w); + } + /// + /// Decode the user's input and relate to an existing function. + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The modified workspace string public override String RunCommand(String workSpace, String inputText, String[] line) { if (workSpace == null || inputText == null || line == null || line.Length < 2) { Console.Error.WriteLine("Malformed!"); @@ -41,28 +52,46 @@ namespace AniNIX.Crypto { return workSpace; } + /// + /// Show this help text + /// public override void GetHelp() { Console.WriteLine("Analysis tools help:\nfreq -- Get frequency of characters.\nfreqinfo -- Return the most common English frequencies.\none-to-one -- See if there is a direct correspondence of characters between cipher and workspace.\ndiff a b -- get the difference between two characters\ncharinfo -- get the info about a character"); } - + /// + /// Return a dictionary of letters mapped to the number of letters found in the workSpace + /// + /// the current workSpace + /// The dictionary of frequencies public static Dictionary FindFrequencies(String workSpace) { Dictionary frequencies = new Dictionary(); + // For each letter in the workSpace,... for (int i = 0; i < workSpace.Length; i++) { if (!Char.IsLetter(workSpace[i])) { continue; } String charStr = String.Format("{0}",workSpace[i]); + // If the letter already exists, increment its frequency. if (frequencies.ContainsKey(charStr)) { frequencies[charStr] = frequencies[charStr] + 1; } else { + // Otherwise add it with a frequency of one. frequencies.Add(charStr,1); } } return frequencies; } + /// + /// Return an ordered list of the most common letters in the workSpace + /// + /// The user workSpace + /// An ordered list public static List GetMostCommonLetters(String workSpace) { + // Find the frequencies. List> freqList = FindFrequencies(workSpace).ToList(); + // Sort the frequencies freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value)); + // Pull out the letters in sorted order and return. List returnL = new List(); foreach (var item in freqList) { returnL.Add(item.Key); @@ -70,8 +99,12 @@ namespace AniNIX.Crypto { return returnL; } + /// Find the doubles in a string + /// the string to analyze + /// a list of doubles public static List GetDoubles(String workSpace) { List theList = new List(); + // For each character in the input, if the previous character is the same, it's a double. Add it to the list. for (int i=1; i + /// Find the substrings of a given length in the workSpace. + /// + /// the workSpace to analyze + /// the length of the substrings to look for + /// the dictionary of substrings by frequency public static Dictionary GetSubstrings(String workSpace, int length) { Dictionary theList = new Dictionary(); + // Start at the beginning of the string, and advance the substring window by one until the substring segment would be outside the workSpace. for (int i=1; i + /// find words of a given length. + /// + /// The length to look for + /// A string broken down by spaces + /// the words of the length with frequencies public static Dictionary FindWordsOfLength(int length,String[] bySpace) { Dictionary wordsFreq = new Dictionary(); + // TODO Replace this with whitespace and punctuation removal + // If the word ends in punctuation and is longer than the length or is equal to the length, add it to the list and track frequency. for (int i = 0; i < bySpace.Length; i++) { if (bySpace[i].Length == length || (bySpace[i].Length == length+1 && Char.IsPunctuation(bySpace[i][length]))) { if (Char.IsPunctuation(bySpace[i][bySpace[i].Length-1])) { @@ -112,6 +164,11 @@ namespace AniNIX.Crypto { return wordsFreq; } + /// + /// Get the top entries in a frequency map + /// + /// Frequency map + /// A list of a given length with the top entries public static List Top(Dictionary theList) { List> freqList = theList.ToList(); List returnL = new List(); @@ -122,7 +179,11 @@ namespace AniNIX.Crypto { return returnL; } - + /// + /// Print an ordered list with the frequency + /// + /// the frequency map + /// String header public static void PrintOrdered(Dictionary theList,String header) { List> freqList = theList.ToList(); freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value)); @@ -133,6 +194,11 @@ namespace AniNIX.Crypto { Console.WriteLine(""); } + /// + /// Print an ordered list + /// + /// the frequency map + /// String header public static void PrintOrdered(List theList,String header) { Console.Write(header); foreach (String str in theList) { @@ -142,6 +208,10 @@ namespace AniNIX.Crypto { Console.WriteLine(); } + /// + /// Analyze a workspace + /// + /// workSpace to analyze public void Frequency(String workSpace) { //Show the individual letter frequeuncy. Console.ForegroundColor = ConsoleColor.Cyan; @@ -164,14 +234,21 @@ namespace AniNIX.Crypto { Console.ResetColor(); } + /// + /// Show the statistical frequencies. + /// public void FrequencyInfo() { // Thanks to http://norvig.com/mayzner.html for this info. // By letter Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Letters by frequency:"); - Console.WriteLine("E T A O I N S R H L D C U M F P G W Y B V K X J Q Z"); + for (int i=0; i < this._sb.EngCommon.Length; i++) { + Console.Write(this._sb.EngCommon[i]); + Console.Write(" "); + } + Console.Write('\n'); Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Possible doubles: LL EE SS OO TT FF RR NN PP CC BB MM GG UU ZZ AA"); + Console.WriteLine("Possible doubles: ll ee ss oo tt ff rr nn pp cc bb mm gg uu zz aa"); // By Substring 2,3 characters in length Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Top sequences of N characters where N=..."); @@ -191,8 +268,14 @@ namespace AniNIX.Crypto { Console.ResetColor(); } - public static void OneToOneQuery(String workSpace, String inputText) { + /// + /// Find out if the workSpace has a one-to-one relationship with the input. + /// + /// the workSpace + /// the user input + public bool OneToOneQuery(String workSpace, String inputText) { Dictionary relation = new Dictionary(); + //Seed the keys so that we print efficiently. StringBuilder subKey = new StringBuilder(); StringBuilder encKey = new StringBuilder(); subKey.Append("True. These are one-to-one.\n"); @@ -200,12 +283,15 @@ namespace AniNIX.Crypto { subKey.Append("sub decrypt "); encKey.Append("sub encrypt "); for (int i = 0; i < workSpace.Length; i++) { + // For each non-whitespace character, if the relation is known, ... if (!Char.IsWhiteSpace(workSpace[i])) { if (relation.ContainsKey(workSpace[i])) { + // if the relation doesn't match up, we found the mismatch and should return false. if (relation[workSpace[i]] != inputText[i]) { Console.Error.WriteLine(String.Format("Character {0} repeated. These are not one-to-one.",workSpace[i])); - return; + return false; } + // Otherwise add the new relation pairing. } else { relation.Add(workSpace[i],inputText[i]); encKey.Append(String.Format("{0}={1} ",inputText[i],workSpace[i])); @@ -213,28 +299,44 @@ namespace AniNIX.Crypto { } } } + // Print the keys and return true. subKey.Append("\nInput-to-final key:"); Console.WriteLine(subKey.ToString()); Console.WriteLine(encKey.ToString()); + return true; } - public void Diff(String[] line) { + /// + /// Show the numeric difference between two characters -- useful for identifying Caesarian ciphers + /// + /// The user's input + /// -99 if malformated or the difference between characters. + public int Diff(String[] line) { + // If the number of arguments or format is wrong, return -99 if (line.Length != 4 || line[2].Length != 1 || line[3].Length != 1) { Console.Error.WriteLine("Bad formatting"); - return; + return -99; } + //Otherwise return -99 char first = line[2][0]; char second = line[3][0]; Console.WriteLine(String.Format("These are different by {0}.",first-second)); + return (first-second); } + /// + /// Printout the info of a character + /// + /// line to analyze public void CharInfo(String[] line) { if (line == null || line.Length != 3 || line[2].Length != 1) { Console.Error.WriteLine("Malformed"); return; } + // Print the ascii value of a character. Console.WriteLine(String.Format("Character: {0}\nASCII Value: {1}",line[2][0],(int)line[2][0])); if (Char.IsLetter(line[2][0])) { + // If the character is a letter, include the alphabet index Console.WriteLine(String.Format("Alphabet index: {0}",(Char.IsUpper(line[2][0])) ? (int)line[2][0] - (int)'A' : (int)line[2][0] - (int)'a')); } } diff --git a/caesarian.csharp b/caesarian.csharp index 403bd98..c15a55f 100644 --- a/caesarian.csharp +++ b/caesarian.csharp @@ -7,12 +7,18 @@ using System.Collections.Generic; namespace AniNIX.Crypto { public class Caesarian : Cipher { - public override String Description() { return "Caesarian cipher suite\nKey format is a numeric shift."; } - public override String Command() { return "caesar"; } + public override String Description() { return "Caesarian cipher suite\nKey format is a numeric shift."; } + public override String Command() { return "caesar"; } + public Caesarian(Workbench w) : base (w) {} - public Caesarian(Workbench w) : base (w) {} - - public override String RunCommand(String workSpace,String inputText,String[] line) { + /// + /// Decode the user's input and relate to an existing function. + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The modified workspace string + 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; @@ -31,12 +37,24 @@ namespace AniNIX.Crypto { } } - public override void GetHelp() { + /// + /// Show this help text + /// + public override void GetHelp() { Console.WriteLine(String.Format("Help for the {0} cipher suite.\n{1}\n",Command(),Description())); Console.WriteLine("encrypt key -- encrypt with the key\ndecrypt key -- decrypt with the key\nbrute -- brute-force for keys\nhelp -- show this helptext."); } - public override String Encrypt(String workSpace,String inputText,String[] line) { + + /// + /// Encrypt a string with the cipher + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The encrypted string + public override String Encrypt(String workSpace,String inputText,String[] line) { + // Validate input if (line.Length != 3) { Console.Error.WriteLine("Bad formatting"); return workSpace; @@ -50,31 +68,35 @@ namespace AniNIX.Crypto { return workSpace; } char[] modified = workSpace.ToCharArray(); + + //For each character in the workSpace, rotate it by the offset given. for (int i = 0; i < modified.Length; i++) { if (Char.IsLetter(modified[i])) { - int baseC; - int modC; - if (modified[i] < 'a') { - baseC = (int)'A'; - modC = (int)modified[i] - (int)'A'; - } else { - baseC = (int)'a'; - modC = (int)modified[i] - (int)'a'; - } - modC = (modC + rotation)%26; - //Debugging - //Console.Write(String.Format("Updating index {0} <{5}> val {1} base {2} mod {3} rotation {4} --to-- ",i,(int)modified[i],baseC,modC,rotation,modified[i])); - modified[i] = (char)(baseC+modC); - //Console.WriteLine(String.Format("<{0}> val {1}",modified[i],baseC+modC)); + int baseC; + int modC; + // Have to account for case. + if (modified[i] < 'a') { + baseC = (int)'A'; + modC = (int)modified[i] - (int)'A'; + } else { + baseC = (int)'a'; + modC = (int)modified[i] - (int)'a'; + } + modC = (modC + rotation)%26; + modified[i] = (char)(baseC+modC); } } return new String(modified); } + + // This is a dummy override. + public override String Decrypt(String workSpace,String inputText,String[] line) { + return Encrypt(workSpace,inputText,line); + } - public override String Decrypt(String workSpace,String inputText,String[] line) { - return Encrypt(workSpace,inputText,line); - } - + /// + /// Try rotating through all 26 possible combinations. + /// public void BruteForce(String workSpace) { String[] line = new String[3]; line[0] = "rot"; diff --git a/chargrid.csharp b/chargrid.csharp index 6b74a8d..ac83853 100644 --- a/chargrid.csharp +++ b/chargrid.csharp @@ -24,6 +24,12 @@ namespace AniNIX.Crypto { return input+(new String(pad)); } + /// + /// Create a grid from the input and row length + /// + /// The string to make the grid from + /// The width of a row + /// a grid private char[][] MakeGrid(String input, int width) { int k=0; int y=(input.Length%width == 0) ? input.Length/width : input.Length/width+1; @@ -39,6 +45,12 @@ namespace AniNIX.Crypto { return newGrid; } + /// + /// Create a grid from a width and length + /// + /// The length of a column + /// The width of a row + /// a grid private char[][] MakeVGrid(int length, int width) { int y = (length%width == 0) ? length/width : length/width+1; char[][] newGrid = new char[y][]; @@ -82,12 +94,19 @@ namespace AniNIX.Crypto { } + /// + /// Create a string representation + /// + /// representation public override String ToString() { StringBuilder sb = new StringBuilder(); + // Include a line to indicate height vs. width sb.Append(String.Format("{0} {1} ------------->\n",theGrid.Length,theGrid[0].Length)); + // Iterate through the arrays for (int j=0; j + /// Return the array for manipulation + /// + /// the array public char[][] ToArray() { return theGrid; } diff --git a/columntransposition.csharp b/columntransposition.csharp index 9872beb..72f2ee5 100644 --- a/columntransposition.csharp +++ b/columntransposition.csharp @@ -8,11 +8,15 @@ namespace AniNIX.Crypto { public override String Description() { return "Column Transposition cipher suite\nFormat is col key1 [key2...]\nThe key format is any word to use for the transposition.\nThis cipher will use an irregular columnar transposition, without padding the input string.\n"; } public override String Command() { return "col"; } - public ColumnTransposition(Workbench w) : base (w) {} public ColumnTransposition() {} + /// + /// Get the order of columns from a key + /// + /// an array of ints indicating order private int[] GetColumnOrder(String key) { + // Create an ordered list and sort from the key. List orderList = new List(); for (int i = 0; i < key.Length; i++) { orderList.Add(key[i]); @@ -21,6 +25,7 @@ namespace AniNIX.Crypto { char[] charArr = orderList.ToArray(); int[] returnOrderIndexes = new int[key.Length]; Console.Write("Found key order: "); + // for each character in the key, find the index in tke sorted array for (int i = 0; i < key.Length; i++) { for (int j = 0; j < charArr.Length; j++) { if (key[i] == charArr[j]) { @@ -34,18 +39,29 @@ namespace AniNIX.Crypto { return returnOrderIndexes; } + /// + /// Encrypt a string with the cipher. See https://en.wikipedia.org/wiki/Transposition_cipher#Columnar_transposition for an example of how this works. + /// + /// 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; } + //Remove newlines. String workSpaceNoNewline = workSpace.Replace("\n",""); char[] changed = workSpaceNoNewline.ToCharArray(); + // Create a grid from the key. CharGrid cg = new CharGrid(workSpaceNoNewline,line[2].Length,false); char[][] encryptionGrid = cg.ToArray(); + //Get the key order. int[] keyOrder = GetColumnOrder(line[2]); Console.Write(cg.ToString()); int k = 0; + // Replace each character by the character in the right place in the character grid. for (int j = 0; j < encryptionGrid[0].Length; j++) { for (int i = 0; i < encryptionGrid.Length; i++) { if (i != (encryptionGrid.Length-1) || keyOrder[j] < encryptionGrid[i].Length) { @@ -55,6 +71,7 @@ namespace AniNIX.Crypto { } } String toReturn = new String(changed); + //Re-insert newlines. for (k = 0; k < workSpace.Length; k++) { if (workSpace[k] == '\n') { toReturn = toReturn.Insert(k,"\n"); @@ -63,22 +80,33 @@ namespace AniNIX.Crypto { return toReturn; } - // TODO + /// + /// Decrypt a string with the cipher + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The decrypted string public override String Decrypt(String workSpace, String cipher, String[] line) { if (line.Length < 3) { Console.Error.WriteLine("Bad formatting."); return workSpace; } + // Remove newlines. String workSpaceNoNewline = workSpace.Replace("\n",""); + // Find the key order. int[] keyOrder = GetColumnOrder(line[2]); + // Create a new chargrid to solve from. CharGrid cg = new CharGrid(workSpaceNoNewline,line[2].Length,keyOrder); Console.Write(cg.ToString()); char[][] cgArray = cg.ToArray(); StringBuilder sb = new StringBuilder(); + // For each row, read the row and add to the solution. for (int i=0; i < cgArray.Length; i++) { sb.Append(new String(cgArray[i])); } String toReturn = sb.ToString(); + // Add back in the newlines. for (int i=0; i < workSpace.Length; i++) { if (workSpace[i] == '\n') { toReturn = toReturn.Insert(i,"\n"); diff --git a/cryptoworkbench.csharp b/cryptoworkbench.csharp index 0b7f3d0..910dacc 100644 --- a/cryptoworkbench.csharp +++ b/cryptoworkbench.csharp @@ -11,6 +11,7 @@ namespace AniNIX.Crypto { 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 Substitution _sub; private Analysis _analysis; private Simple _simple; @@ -19,19 +20,28 @@ namespace AniNIX.Crypto { private Vigenere _vig; private ColumnTransposition _col; private Ubchi _ubchi; + // 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."); 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]); @@ -45,6 +55,7 @@ namespace AniNIX.Crypto { 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; @@ -55,24 +66,34 @@ namespace AniNIX.Crypto { } + /// + /// 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 { 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("You can get help on any command by running \" help\".\nSuppress printing the cipher with a trailing ;.\nAvailable 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"); @@ -81,8 +102,11 @@ namespace AniNIX.Crypto { HelpText.Append("reset -- reset workspace to the ciphertext.\n"); HelpText.Append("links -- show some helpful links\n"); HelpText.Append("help -- show this HelpText\n"); + HelpText.Append("print -- show the current workspace\n"); + HelpText.Append("display -- alias of print\n"); HelpText.Append("exit -- exit and show the result.\n"); HelpText.Append("quit -- alias of exit.\n"); + // Initialize the ciphersuites. _sub = new Substitution(this); _analysis = new Analysis(this); _simple = new Simple(this); @@ -94,6 +118,9 @@ namespace AniNIX.Crypto { } + /// + /// Convert this workbench into a readable string. + /// public override String ToString() { StringBuilder currentStatus = new StringBuilder(); currentStatus.Append("Input:\n"); @@ -104,6 +131,9 @@ namespace AniNIX.Crypto { return currentStatus.ToString(); } + /// + /// Display this workbench to stdout with colors. + /// public void Print() { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Input:"); @@ -135,7 +165,10 @@ namespace AniNIX.Crypto { } 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"); @@ -144,42 +177,61 @@ namespace AniNIX.Crypto { 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(); try { + // Set the initial command to be show the helptext. string command = "help"; string read = "help"; 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; + 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); @@ -211,6 +263,10 @@ namespace AniNIX.Crypto { case "help": Console.WriteLine(HelpText.ToString()); break; + case "display": + case "print": + showCipher = true; + break; case "exit": case "quit": throw new Exception(""); @@ -223,7 +279,10 @@ namespace AniNIX.Crypto { break; } } + + // Show the cipher if the user asked. if (showCipher) Print(); + // Display an AniNIX-standard prompt. Console.Write("\nWhat command would you like to execute?\n"); Console.ForegroundColor = ConsoleColor.Red; Console.Write("|"); @@ -231,16 +290,22 @@ namespace AniNIX.Crypto { 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 { diff --git a/substitution.csharp b/substitution.csharp index c3a1681..b75172e 100644 --- a/substitution.csharp +++ b/substitution.csharp @@ -49,6 +49,13 @@ namespace AniNIX.Crypto { Console.WriteLine("encrypt key[s] -- encrypt with the key[s]\ndecrypt 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."); @@ -58,17 +65,17 @@ namespace AniNIX.Crypto { 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...", diff --git a/ubchi.csharp b/ubchi.csharp index 491ea43..87a2a27 100644 --- a/ubchi.csharp +++ b/ubchi.csharp @@ -14,25 +14,42 @@ namespace AniNIX.Crypto { private ColumnTransposition col = new ColumnTransposition(); + /// + /// Encrypt a string with the cipher + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The encrypted string public override String Encrypt(String workSpace,String inputText,String[] line) { if (line == null || line.Length != 3) { Console.Error.WriteLine("Malformed!"); return workSpace; } + // Pad the incoming workspace String changed = CharGrid.RandPad(workSpace,line[2].Length); + // Transpose twice. changed = col.Encrypt(changed,inputText,line); changed = col.Encrypt(changed,inputText,line); return changed; } + /// + /// Decrypt a string with the cipher + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The encrypted string public override String Decrypt(String workSpace,String inputText,String[] line) { if (line == null || line.Length != 3) { Console.Error.WriteLine("Malformed!"); return workSpace; } + // De-transpose twice. Without encrypting a number, we don't have a way to programmatically trim. String changed = col.Decrypt(workSpace,inputText,line); changed = col.Decrypt(changed,inputText,line); - return changed; // TODO Remove padding + return changed; } } diff --git a/vigenere.csharp b/vigenere.csharp index 8ca0de4..1434093 100644 --- a/vigenere.csharp +++ b/vigenere.csharp @@ -12,6 +12,13 @@ namespace AniNIX.Crypto { public Vigenere(Workbench w) : base (w) {} + /// + /// Encrypt a string with the cipher. Encryption and decryption work like a substitution but is rotated with a key. + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The encrypted string public override String Encrypt(String workSpace,String inputText,String[] line) { if (line == null || line.Length != 3) { Console.Error.WriteLine("Malformed!"); @@ -27,6 +34,7 @@ namespace AniNIX.Crypto { return workSpace; } } + // For each letter in the workspace, substitute with an offset given by the key. Rotate which letter in the key is used. for (int i = 0; i < changed.Length; i++) { if (Char.IsLetter(changed[i])) { int baseC = (Char.IsUpper(changed[i])) ? (int)'A' : (int)'a'; @@ -44,6 +52,13 @@ namespace AniNIX.Crypto { return new String(changed); } + /// + /// Decrypt a string with the cipher. Like the encryption method, we decrypt by removing the offset as given by the key. + /// + /// The working copy of the cipher + /// The original cipher + /// The user input + /// The decrypted string public override String Decrypt(String workSpace,String inputText,String[] line) { if (line == null || line.Length != 3) { Console.Error.WriteLine("Malformed!");