Commenting, some style fixes for output

This commit is contained in:
DarkFeather 2016-09-21 19:10:34 -05:00
parent 65dbd8cae2
commit 78e3ffe82f
10 changed files with 365 additions and 45 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
CryptoApplet.java
cryptoworkbench.exe

View File

@ -11,21 +11,33 @@ namespace AniNIX.Crypto {
public override String Command() {return "affine";}
public Affine(Workbench w) : base (w) {}
/// <summary>
/// Encrypt a string with the Affine cipher
/// </summary>
/// <param name="workspace">A parameter for X</param>
/// <param name="inputText">A parameter for Y</param>
/// <param name="line">The line given by the user</param>
/// <returns>The return value</returns>
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) {
/// <summary>
/// Find the multiplicative inverse of a number.
/// </summary>
/// <param name="a">A number</param>
/// <returns>The inverse</returns>
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.");
}
/// <summary>
/// Decrypt a string with the cipher
/// </summary>
/// <param name="workSpace">A parameter for X</param>
/// <param name="inputText">A parameter for Y</param>
/// <param name="line">The user input</param>
/// <returns>The decrypted string</returns>
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';

View File

@ -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);
}
/// <summary>
/// Decode the user's input and relate to an existing function.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The modified workspace string</returns>
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;
}
/// <summary>
/// Show this help text
/// </summary>
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");
}
/// <summary>
/// Return a dictionary of letters mapped to the number of letters found in the workSpace
/// </summary>
/// <param name="workSpace">the current workSpace</param>
/// <returns>The dictionary of frequencies</returns>
public static Dictionary<String,int> FindFrequencies(String workSpace) {
Dictionary<String,int> frequencies = new Dictionary<String,int>();
// 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;
}
/// <summary>
/// Return an ordered list of the most common letters in the workSpace
/// </summary>
/// <param name="workSpace">The user workSpace</param>
/// <returns>An ordered list</returns>
public static List<String> GetMostCommonLetters(String workSpace) {
// Find the frequencies.
List<KeyValuePair<String,int>> 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<String> returnL = new List<String>();
foreach (var item in freqList) {
returnL.Add(item.Key);
@ -70,8 +99,12 @@ namespace AniNIX.Crypto {
return returnL;
}
/// <summary> Find the doubles in a string </summary>
/// <param name="workSpace"> the string to analyze </param>
/// <returns> a list of doubles</returns>
public static List<String> GetDoubles(String workSpace) {
List<String> theList = new List<String>();
// 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<workSpace.Length; i++) {
if (workSpace[i] == workSpace[i-1] && !theList.Contains(workSpace[i].ToString())) {
theList.Add(workSpace[i].ToString());
@ -80,12 +113,23 @@ namespace AniNIX.Crypto {
return theList;
}
/// <summary>
/// Find the substrings of a given length in the workSpace.
/// </summary>
/// <param name="workSpace">the workSpace to analyze</param>
/// <param name="length"> the length of the substrings to look for</param>
/// <returns>the dictionary of substrings by frequency</returns>
public static Dictionary<String,int> GetSubstrings(String workSpace, int length) {
Dictionary<string,int> theList = new Dictionary<string,int>();
// 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<workSpace.Length-(length-1); i++) {
// Get the substring
String segment = workSpace.Substring(i,length);
// Remove whitespace
segment = Regex.Replace(segment, @"[^\w]", string.Empty);
// If the segment is no longer the length, bypass.
if (segment.Length != length) continue;
// Otherwise add or increment the segment's frequency.
if (theList.ContainsKey(segment)) {
theList[segment] += 1;
} else {
@ -95,8 +139,16 @@ namespace AniNIX.Crypto {
return theList;
}
/// <summary>
/// find words of a given length.
/// </summary>
/// <param name="length"> The length to look for </length>
/// <param name="bySpace"> A string broken down by spaces</param>
/// <returns> the words of the length with frequencies </returns>
public static Dictionary<String,int> FindWordsOfLength(int length,String[] bySpace) {
Dictionary<String,int> wordsFreq = new Dictionary<String,int>();
// 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;
}
/// <summary>
/// Get the top entries in a frequency map
/// </summary>
/// <param name="theList">Frequency map</param>
/// <returns>A list of a given length with the top entries</returns>
public static List<String> Top(Dictionary<String,int> theList) {
List<KeyValuePair<string,int>> freqList = theList.ToList();
List<String> returnL = new List<String>();
@ -122,7 +179,11 @@ namespace AniNIX.Crypto {
return returnL;
}
/// <summary>
/// Print an ordered list with the frequency
/// </summary>
/// <param name="theList"> the frequency map </param>
/// <param name="header"> String header </param>
public static void PrintOrdered(Dictionary<String,int> theList,String header) {
List<KeyValuePair<string,int>> freqList = theList.ToList();
freqList.Sort((firstPair,nextPair)=>nextPair.Value.CompareTo(firstPair.Value));
@ -133,6 +194,11 @@ namespace AniNIX.Crypto {
Console.WriteLine("");
}
/// <summary>
/// Print an ordered list
/// </summary>
/// <param name="theList"> the frequency map </param>
/// <param name="header"> String header </param>
public static void PrintOrdered(List<String> theList,String header) {
Console.Write(header);
foreach (String str in theList) {
@ -142,6 +208,10 @@ namespace AniNIX.Crypto {
Console.WriteLine();
}
///<summary>
/// Analyze a workspace
/// <summary>
/// <param name="workSpace">workSpace to analyze</param>
public void Frequency(String workSpace) {
//Show the individual letter frequeuncy.
Console.ForegroundColor = ConsoleColor.Cyan;
@ -164,14 +234,21 @@ namespace AniNIX.Crypto {
Console.ResetColor();
}
/// <summary>
/// Show the statistical frequencies.
/// </summary>
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) {
/// <summary>
/// Find out if the workSpace has a one-to-one relationship with the input.
/// </summary>
/// <param name="workSpace">the workSpace</param>
/// <param name="inputText">the user input</param>
public bool OneToOneQuery(String workSpace, String inputText) {
Dictionary<char,char> relation = new Dictionary<char,char>();
//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) {
/// <summary>
/// Show the numeric difference between two characters -- useful for identifying Caesarian ciphers
/// </summary>
/// <param name="line">The user's input</param>
/// <returns>-99 if malformated or the difference between characters.</returns>
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);
}
/// <summary>
/// Printout the info of a character
/// </summary>
/// <param name="line">line to analyze</param>
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'));
}
}

View File

@ -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) {
/// <summary>
/// Decode the user's input and relate to an existing function.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The modified workspace string</returns>
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() {
/// <summary>
/// Show this help text
/// </summary>
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) {
/// <summary>
/// Encrypt a string with the cipher
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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);
}
/// <summary>
/// Try rotating through all 26 possible combinations.
/// </summary>
public void BruteForce(String workSpace) {
String[] line = new String[3];
line[0] = "rot";

View File

@ -24,6 +24,12 @@ namespace AniNIX.Crypto {
return input+(new String(pad));
}
/// <summary>
/// Create a grid from the input and row length
/// </summary>
/// <param name="input">The string to make the grid from</param>
/// <param name="width">The width of a row</param>
/// <returns>a grid</returns>
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;
}
/// <summary>
/// Create a grid from a width and length
/// </summary>
/// <param name="length">The length of a column </param>
/// <param name="width">The width of a row</param>
/// <returns>a grid</returns>
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 {
}
/// <summary>
/// Create a string representation
/// </summary>
/// <returns>representation</returns>
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<theGrid.Length; j++) {
sb.Append("| ");
for (int i=0; i<theGrid[j].Length; i++) {
// Print the letters as either letters or ASCII codes.
if (Char.IsLetter(theGrid[j][i])) {
sb.Append(String.Format("{0} ",theGrid[j][i]));
} else {
@ -100,6 +119,10 @@ namespace AniNIX.Crypto {
return sb.ToString();
}
/// <summary>
/// Return the array for manipulation
/// </summary>
/// <returns>the array</returns>
public char[][] ToArray() {
return theGrid;
}

View File

@ -8,11 +8,15 @@ namespace AniNIX.Crypto {
public override String Description() { return "Column Transposition cipher suite\nFormat is col <command> 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() {}
/// <summary>
/// Get the order of columns from a key
/// </summary>
/// <returns>an array of ints indicating order</returns>
private int[] GetColumnOrder(String key) {
// Create an ordered list and sort from the key.
List<char> orderList = new List<char>();
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;
}
/// <summary>
/// Encrypt a string with the cipher. See https://en.wikipedia.org/wiki/Transposition_cipher#Columnar_transposition for an example of how this works.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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
/// <summary>
/// Decrypt a string with the cipher
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The decrypted string</returns>
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");

View File

@ -11,6 +11,7 @@ namespace AniNIX.Crypto {
public string workSpace { get; private set; }
public StringBuilder HelpText = new StringBuilder();
public Dictionary<String,Cipher> SwitchCases = new Dictionary<String,Cipher>();
// 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;
/// <summary>
/// Read in a cipher either from a filename or from stdin
/// <summary>
/// <param name="line">user input</param>
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 {
}
/// <summary>
/// Create a new workbench from input.
/// </summary>
/// <param name="args">the arguments provided for execution</param>
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 \"<command> 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 {
}
/// <summary>
/// Convert this workbench into a readable string.
/// </summary>
public override String ToString() {
StringBuilder currentStatus = new StringBuilder();
currentStatus.Append("Input:\n");
@ -104,6 +131,9 @@ namespace AniNIX.Crypto {
return currentStatus.ToString();
}
/// <summary>
/// Display this workbench to stdout with colors.
/// </summary>
public void Print() {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Input:");
@ -135,7 +165,10 @@ namespace AniNIX.Crypto {
}
Console.WriteLine();
}
/// <summary>
/// Show some helpful links.
/// </summary>
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());
}
/// <summary>
/// Save the workspace to a file
/// </summary>
/// <param name="workSpace"> what to write </param>
/// <param name="line"> user input, which should include a file</param>
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));
}
}
/// <summary>
/// Interact with the user.
/// </summary>
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();
}
}
/// <summary>
/// Create and execute a workbench program.
/// </summary>
public static void Main(string[] args) {
Workbench cw = new Workbench(args);
try {

View File

@ -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.");
}
/// <summary>
/// Encrypt a string with the cipher, replacing one character with another.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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<line.Length;i++) {
if (line[i].Length < 3 || line[i].Length%2 != 1 || line[i][line[i].Length/2] != '=') {
Console.Error.WriteLine("Bad substitution. Aborting.");
/* Console.Error.WriteLine(String.Format("Line length: {0}",line[i].Length));
* Console.Error.WriteLine(String.Format("Line mod 2: {0}",line[i].Length%2));
* Console.Error.WriteLine(String.Format("Line[length/2+1]: {0}",line[i][line[i].Length/2+1]));
*/
return workSpace;
}
// For each key-value pair...
for (int k = 0; k < line[i].Length/2; k++) {
char oldS = line[i].Substring(k,1)[0];
char newS = line[i].Substring(k+line[i].Length/2+1,1)[0];
Console.WriteLine(String.Format("Replacing cipher {0} to be workspace {1}",oldS,newS));
// for each character in the workspace...
for (int j = 0; j < workSpace.Length; j++) {
// replace the old character with the requested replacement.
if (cipher[j] == oldS) {
changed[j] = newS;
}
@ -78,14 +85,24 @@ namespace AniNIX.Crypto {
return new String(changed);
}
// This is a dummy for encrypt -- the functions are the same.
public override String Decrypt(String workSpace, String cipher, String[] line) {
return Encrypt(workSpace, cipher, line);
}
/// <summary>
/// Perform a frequency analysis and see if ETAOIN substitution will solve the problem. TODO this should look at a dictionary for confirmation.
/// </summary>
/// <param name="workSpace"> the string to analyze and brute-force</param>
/// <returns> an attempted solution. </returns>
public String TryCommon(String workSpace) {
// Get the frequency analysis
List<String> 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...",

View File

@ -14,25 +14,42 @@ namespace AniNIX.Crypto {
private ColumnTransposition col = new ColumnTransposition();
/// <summary>
/// Encrypt a string with the cipher
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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;
}
/// <summary>
/// Decrypt a string with the cipher
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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;
}
}

View File

@ -12,6 +12,13 @@ namespace AniNIX.Crypto {
public Vigenere(Workbench w) : base (w) {}
/// <summary>
/// Encrypt a string with the cipher. Encryption and decryption work like a substitution but is rotated with a key.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The encrypted string</returns>
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);
}
/// <summary>
/// Decrypt a string with the cipher. Like the encryption method, we decrypt by removing the offset as given by the key.
/// </summary>
/// <param name="workSpace">The working copy of the cipher</param>
/// <param name="inputText">The original cipher</param>
/// <param name="line">The user input</param>
/// <returns>The decrypted string</returns>
public override String Decrypt(String workSpace,String inputText,String[] line) {
if (line == null || line.Length != 3) {
Console.Error.WriteLine("Malformed!");