Commenting, some style fixes for output
This commit is contained in:
parent
65dbd8cae2
commit
78e3ffe82f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
CryptoApplet.java
|
||||
cryptoworkbench.exe
|
||||
|
@ -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';
|
||||
|
120
analysis.csharp
120
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);
|
||||
}
|
||||
|
||||
/// <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'));
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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...",
|
||||
|
19
ubchi.csharp
19
ubchi.csharp
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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!");
|
||||
|
Loading…
Reference in New Issue
Block a user