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!");