Fixed sonarqube findings
This commit is contained in:
296
src/main/java/com/mattrixwv/cipherstream/combination/ADFGVX.java
Normal file
296
src/main/java/com/mattrixwv/cipherstream/combination/ADFGVX.java
Normal file
@@ -0,0 +1,296 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/combination/ADFGVX.java
|
||||
//Mattrixwv
|
||||
// Created: 01-26-22
|
||||
//Modified: 07-03-22
|
||||
package com.mattrixwv.cipherstream.combination;
|
||||
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
import com.mattrixwv.cipherstream.polysubstitution.Columnar;
|
||||
import com.mattrixwv.cipherstream.polysubstitution.PolybiusSquare;
|
||||
|
||||
|
||||
public class ADFGVX{
|
||||
private class LargePolybiusSquare extends PolybiusSquare{
|
||||
@Override
|
||||
protected void createGrid(){
|
||||
for(int row = 0;row < 6;++row){
|
||||
for(int col = 0;col < 6;++col){
|
||||
char letter = keyword.charAt((6 * row) + col);
|
||||
grid[row][col] = letter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void setInputStringEncoding(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Change to upper case
|
||||
inputString = inputString.toUpperCase();
|
||||
|
||||
//Remove any whitespace if selected
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
|
||||
//Remove any symbols if selected
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z0-9\\s]", "");
|
||||
}
|
||||
|
||||
if(!preserveWhitespace && !preserveSymbols){
|
||||
//Add whitespace after every character for the default look
|
||||
StringJoiner spacedString = new StringJoiner(" ");
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
spacedString.add(Character.toString(inputString.charAt(cnt)));
|
||||
}
|
||||
inputString = spacedString.toString();
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank() || getPreparedInputStringEncoding().isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected String getPreparedInputStringEncoding(){
|
||||
String cleanString = inputString.toUpperCase();
|
||||
cleanString = cleanString.replaceAll("[^A-Z0-9]", "");
|
||||
return cleanString;
|
||||
}
|
||||
@Override
|
||||
protected void setKeyword(String keyword){
|
||||
if(keyword == null){
|
||||
throw new NullPointerException("Keyword cannot be null");
|
||||
}
|
||||
//Change everything to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
|
||||
//Remove everything except capital letters and numbers
|
||||
keyword = keyword.replaceAll("[^A-Z0-9]", "");
|
||||
|
||||
//Add all letters in the alphabet to the key
|
||||
keyword += "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
//Remove all duplicate characters
|
||||
StringBuilder uniqueKey = new StringBuilder();
|
||||
keyword.chars().distinct().forEach(c -> uniqueKey.append((char)c));
|
||||
this.keyword = uniqueKey.toString();
|
||||
|
||||
//Create the grid from the sanitized keyword
|
||||
createGrid();
|
||||
}
|
||||
@Override
|
||||
protected void addCharactersToCleanStringEncode(String cleanString){
|
||||
int outputCnt = 0;
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
//Add both numbers of any letters to the output
|
||||
if(Character.isAlphabetic(inputString.charAt(inputCnt)) || Character.isDigit(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
}
|
||||
//Add any other characters that appear to the output
|
||||
else{
|
||||
fullOutput.append(inputString.charAt(inputCnt));
|
||||
}
|
||||
}
|
||||
outputString = fullOutput.toString();
|
||||
}
|
||||
@Override
|
||||
protected void addCharactersToCleanStringDecode(String cleanString){
|
||||
int outputCnt = 0;
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
//Add the letter to the output and skip the second number
|
||||
if(Character.isDigit(inputString.charAt(inputCnt)) || Character.isAlphabetic(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
++inputCnt;
|
||||
}
|
||||
//Add any other characters that appear to the output
|
||||
else{
|
||||
fullOutput.append(inputString.charAt(inputCnt));
|
||||
}
|
||||
}
|
||||
outputString = fullOutput.toString();
|
||||
}
|
||||
@Override
|
||||
public void reset(){
|
||||
grid = new char[6][6];
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
|
||||
public LargePolybiusSquare(boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
super(preserveWhitespace, preserveSymbols);
|
||||
}
|
||||
}
|
||||
private boolean preserveCapitals;
|
||||
private boolean preserveWhitespace;
|
||||
private boolean preserveSymbols;
|
||||
private String inputString;
|
||||
private String outputString;
|
||||
private String squareKeyword;
|
||||
private String keyword;
|
||||
private LargePolybiusSquare largePolybiusSquare;
|
||||
private Columnar columnar;
|
||||
|
||||
private void setSquareKeyword(String squareKeyword) throws InvalidKeywordException{
|
||||
if(squareKeyword == null){
|
||||
throw new InvalidKeywordException("Square Keyword cannot be null");
|
||||
}
|
||||
|
||||
this.squareKeyword = squareKeyword;
|
||||
}
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
this.keyword = keyword;
|
||||
}
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
private void formatOutputStringEncode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
int outputLocation = 0;
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
}
|
||||
private void formatOutputStringDecode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
int outputLocation = 0;
|
||||
for(int inputLocation = 0;inputLocation < inputString.length();++inputLocation){
|
||||
char ch = inputString.charAt(inputLocation);
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
++inputLocation;
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
++inputLocation;
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
outputString = output.toString();
|
||||
}
|
||||
private String encode() throws InvalidCharacterException, InvalidInputException, InvalidKeywordException{
|
||||
//Encode the input with polybius
|
||||
String polybiusOutput = largePolybiusSquare.encode(squareKeyword, inputString);
|
||||
//Change polybius to use the correct symbols
|
||||
polybiusOutput = polybiusOutput.replace("1", "A").replace("2", "D").replace("3", "F").replace("4", "G").replace("5", "V").replace("6", "X");
|
||||
|
||||
//Encode polybius's output with columnar
|
||||
String columnarOutput = columnar.encode(keyword, polybiusOutput);
|
||||
outputString = columnarOutput;
|
||||
|
||||
//Add whatever is needed to the output string
|
||||
formatOutputStringEncode();
|
||||
|
||||
return outputString;
|
||||
}
|
||||
private String decode() throws InvalidKeywordException, InvalidCharacterException, InvalidInputException{
|
||||
//Decode the input with columnar
|
||||
String columnarOutput = columnar.decode(keyword, inputString);
|
||||
|
||||
//Change the symbols to the correct ones for polybius
|
||||
columnarOutput = columnarOutput.replace("A", "1").replace("D", "2").replace("F", "3").replace("G", "4").replace("V", "5").replace("X", "6");
|
||||
//Decode with polybius
|
||||
String polybiusOutput = largePolybiusSquare.decode(squareKeyword, columnarOutput);
|
||||
outputString = polybiusOutput;
|
||||
|
||||
//Add whatever is needed to the output string
|
||||
formatOutputStringDecode();
|
||||
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public ADFGVX() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
reset();
|
||||
}
|
||||
public ADFGVX(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
reset();
|
||||
}
|
||||
|
||||
public String encode(String squareKeyword, String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
setSquareKeyword(squareKeyword);
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
public String decode(String squareKeyword, String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
setSquareKeyword(squareKeyword);
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
return decode();
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getSquareKeyword(){
|
||||
return squareKeyword;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
public void reset() throws InvalidCharacterException{
|
||||
largePolybiusSquare = new LargePolybiusSquare(false, false);
|
||||
columnar = new Columnar(false, false, false, true, 'B');
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
squareKeyword = "";
|
||||
keyword = "";
|
||||
}
|
||||
}
|
||||
193
src/main/java/com/mattrixwv/cipherstream/combination/ADFGX.java
Normal file
193
src/main/java/com/mattrixwv/cipherstream/combination/ADFGX.java
Normal file
@@ -0,0 +1,193 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/combination/ADFGX.java
|
||||
//Mattrixwv
|
||||
// Created: 01-25-22
|
||||
//Modified: 07-03-22
|
||||
package com.mattrixwv.cipherstream.combination;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
import com.mattrixwv.cipherstream.polysubstitution.Columnar;
|
||||
import com.mattrixwv.cipherstream.polysubstitution.PolybiusSquare;
|
||||
|
||||
|
||||
public class ADFGX{
|
||||
private String inputString; //This is the string that needs encoded/decoded
|
||||
private String outputString; //This is the string that is output after encoding/decoding
|
||||
private String squareKeyword; //The keyword used to create the Polybius Square
|
||||
private String keyword; //The keyword used to create the Columnar cipher
|
||||
private boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
private boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
private boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
private PolybiusSquare polybiusSquare; //The first step in encoding
|
||||
private Columnar columnar; //The second step in encoding
|
||||
|
||||
//Ensures Polybius keyword constraints
|
||||
private void setSquareKeyword(String squareKeyword) throws InvalidKeywordException{
|
||||
if(squareKeyword == null){
|
||||
throw new InvalidKeywordException("Square Keyword cannot be null");
|
||||
}
|
||||
|
||||
this.squareKeyword = squareKeyword;
|
||||
}
|
||||
//Ensures Columnar keyword constraints
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
this.keyword = keyword;
|
||||
}
|
||||
//Ensures inputString constraints
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
//Format the output string with capitals, symbols, and numbers that are in the input string
|
||||
private void formatOutputStringEncode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
int outputLocation = 0;
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
}
|
||||
private void formatOutputStringDecode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
int outputLocation = 0;
|
||||
for(int inputLocation = 0;inputLocation < inputString.length();++inputLocation){
|
||||
char ch = inputString.charAt(inputLocation);
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLocation++)));
|
||||
++inputLocation;
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLocation++)));
|
||||
++inputLocation;
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
outputString = output.toString();
|
||||
}
|
||||
//Encodes the inputString and stores the result in outputString
|
||||
private void encode() throws InvalidCharacterException, InvalidInputException, InvalidKeywordException{
|
||||
//Encode the input with polybius
|
||||
String polybiusOutput = polybiusSquare.encode(squareKeyword, inputString);
|
||||
squareKeyword = polybiusSquare.getKeyword();
|
||||
//Change polybius to use the correct symbols
|
||||
polybiusOutput = polybiusOutput.replace("1", "A").replace("2", "D").replace("3", "F").replace("4", "G").replace("5", "X");
|
||||
|
||||
//Encode polybius's output with columnar
|
||||
String columnarOutput = columnar.encode(keyword, polybiusOutput);
|
||||
keyword = columnar.getKeyword();
|
||||
outputString = columnarOutput;
|
||||
|
||||
//Add whatever is needed to the output string
|
||||
formatOutputStringEncode();
|
||||
}
|
||||
//Decodes the inputString and stores the result in outputString
|
||||
private void decode() throws InvalidKeywordException, InvalidCharacterException, InvalidInputException{
|
||||
//Decode the input with columnar
|
||||
String columnarOutput = columnar.decode(keyword, inputString);
|
||||
keyword = columnar.getKeyword();
|
||||
|
||||
//Change the symbols to the correct ones for polybius
|
||||
columnarOutput = columnarOutput.replace("A", "1").replace("D", "2").replace("F", "3").replace("G", "4").replace("X", "5");
|
||||
//Decode with polybius
|
||||
String polybiusOutput = polybiusSquare.decode(squareKeyword, columnarOutput);
|
||||
squareKeyword = polybiusSquare.getKeyword();
|
||||
outputString = polybiusOutput;
|
||||
|
||||
//Add whatever is needed to the output string
|
||||
formatOutputStringDecode();
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public ADFGX() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
reset();
|
||||
}
|
||||
public ADFGX(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
reset();
|
||||
}
|
||||
|
||||
//Encodes inputString using keyword and returns the result
|
||||
public String encode(String squareKeyword, String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
setSquareKeyword(squareKeyword);
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using keyword and returns the result
|
||||
public String decode(String squareKeyword, String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
setSquareKeyword(squareKeyword);
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Returns the cleaned inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns the outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Returns the cleaned Polybius Square keyword
|
||||
public String getSquareKeyword(){
|
||||
return squareKeyword;
|
||||
}
|
||||
//Returns the cleaned Columnar keyword
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset() throws InvalidCharacterException{
|
||||
polybiusSquare = new PolybiusSquare(false, false);
|
||||
columnar = new Columnar(false, false, false, true, 'B');
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
squareKeyword = "";
|
||||
keyword = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Exceptions/InvalidBaseException.java
|
||||
//Mattrixwv
|
||||
// Created: 01-09-22
|
||||
//Modified: 01-09-22
|
||||
package com.mattrixwv.cipherstream.exceptions;
|
||||
|
||||
|
||||
public class InvalidBaseException extends RuntimeException{
|
||||
public InvalidBaseException(){
|
||||
super();
|
||||
}
|
||||
public InvalidBaseException(String message){
|
||||
super(message);
|
||||
}
|
||||
public InvalidBaseException(Throwable error){
|
||||
super(error);
|
||||
}
|
||||
public InvalidBaseException(String message, Throwable error){
|
||||
super(message, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Exceptions/InvalidCharacterException.java
|
||||
//Mattrixwv
|
||||
// Created: 01-04-22
|
||||
//Modified: 01-04-22
|
||||
package com.mattrixwv.cipherstream.exceptions;
|
||||
|
||||
|
||||
public class InvalidCharacterException extends RuntimeException{
|
||||
public InvalidCharacterException(){
|
||||
super();
|
||||
}
|
||||
public InvalidCharacterException(String message){
|
||||
super(message);
|
||||
}
|
||||
public InvalidCharacterException(Throwable error){
|
||||
super(error);
|
||||
}
|
||||
public InvalidCharacterException(String message, Throwable error){
|
||||
super(message, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Exceptions/InvalidInputException.java
|
||||
//Mattrixwv
|
||||
// Created: 01-09-22
|
||||
//Modified: 01-09-22
|
||||
package com.mattrixwv.cipherstream.exceptions;
|
||||
|
||||
|
||||
public class InvalidInputException extends RuntimeException{
|
||||
public InvalidInputException(){
|
||||
super();
|
||||
}
|
||||
public InvalidInputException(String message){
|
||||
super(message);
|
||||
}
|
||||
public InvalidInputException(Throwable error){
|
||||
super(error);
|
||||
}
|
||||
public InvalidInputException(String message, Throwable error){
|
||||
super(message, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Exceptions/InvalidKeywordException.java
|
||||
//Mattrixwv
|
||||
// Created: 01-09-22
|
||||
//Modified: 01-09-22
|
||||
package com.mattrixwv.cipherstream.exceptions;
|
||||
|
||||
|
||||
public class InvalidKeywordException extends RuntimeException{
|
||||
public InvalidKeywordException(){
|
||||
super();
|
||||
}
|
||||
public InvalidKeywordException(String message){
|
||||
super(message);
|
||||
}
|
||||
public InvalidKeywordException(Throwable error){
|
||||
super(error);
|
||||
}
|
||||
public InvalidKeywordException(String message, Throwable error){
|
||||
super(message, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Affine.java
|
||||
//Mattrixwv
|
||||
// Created: 01-26-22
|
||||
//Modified: 02-17-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
import mattrixwv.NumberAlgorithms;
|
||||
|
||||
|
||||
public class Affine{
|
||||
private boolean preserveCapitals;
|
||||
private boolean preserveSymbols;
|
||||
private boolean preserveWhitespace;
|
||||
private String inputString;
|
||||
private String outputString;
|
||||
private int key1; //Key1 must be relatively prime to 26
|
||||
private int key2;
|
||||
|
||||
private void setKey1(int key1) throws InvalidKeywordException{
|
||||
//If the key is negative change it to possitive
|
||||
if(key1 < 0){
|
||||
key1 %= 26;
|
||||
key1 += 26;
|
||||
}
|
||||
|
||||
//Make sure the key is relatively prime to 26 (The number of letters)
|
||||
if(NumberAlgorithms.gcd(key1, 26) != 1){
|
||||
throw new InvalidKeywordException("Key 1 must be relatively prime to 26");
|
||||
}
|
||||
|
||||
//Save the key
|
||||
this.key1 = key1;
|
||||
}
|
||||
private void setKey2(int key2){
|
||||
//If the key is negative change it to possitive
|
||||
if(key2 < 0){
|
||||
key2 %= 26;
|
||||
key2 += 26;
|
||||
}
|
||||
|
||||
//Save the key
|
||||
this.key2 = key2;
|
||||
}
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input must not be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toLowerCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
private String encode(){
|
||||
//Step through every character in the input and encode it if needed
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(char ch : inputString.toCharArray()){
|
||||
//Encode all letters
|
||||
if(Character.isUpperCase(ch)){
|
||||
//Change the character to a number
|
||||
int letter = ch - 65;
|
||||
//Encode the number
|
||||
letter = ((key1 * letter) + key2) % 26;
|
||||
//Change the new number back to a character and append it to the output
|
||||
char newChar = (char)(letter + 65);
|
||||
output.append(newChar);
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
//Change the character to a number
|
||||
int letter = ch - 97;
|
||||
//Encode the number
|
||||
letter = ((key1 * letter) + key2) % 26;
|
||||
//Change the new number back to a character and append it to the output
|
||||
char newChar = (char)(letter + 97);
|
||||
output.append(newChar);
|
||||
}
|
||||
//Pass all other characters through
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
private String decode(){
|
||||
//Find the multiplicative inverse of key1
|
||||
int key1I = 1;
|
||||
while(((key1 * key1I) % 26) != 1){
|
||||
++key1I;
|
||||
}
|
||||
|
||||
//Step through every character in the input and decode it if needed
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(char ch : inputString.toCharArray()){
|
||||
//Encode all letters
|
||||
if(Character.isUpperCase(ch)){
|
||||
//Change the letter to a number
|
||||
int letter = ch - 65;
|
||||
//Encode the number
|
||||
letter = (key1I * (letter - key2)) % 26;
|
||||
if(letter < 0){
|
||||
letter += 26;
|
||||
}
|
||||
//Change the new number back to a character and append it to the output
|
||||
char newChar = (char)(letter + 65);
|
||||
output.append(newChar);
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
//Change the letter to a number
|
||||
int letter = ch - 97;
|
||||
//Encode the number
|
||||
letter = (key1I * (letter - key2)) % 26;
|
||||
if(letter < 0){
|
||||
letter += 26;
|
||||
}
|
||||
//Change the new number back to a character and append it to the output
|
||||
char newChar = (char)(letter + 97);
|
||||
output.append(newChar);
|
||||
}
|
||||
//Pass all other characters through
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public Affine(){
|
||||
preserveCapitals = false;
|
||||
preserveSymbols = false;
|
||||
preserveWhitespace = false;
|
||||
reset();
|
||||
}
|
||||
public Affine(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
reset();
|
||||
}
|
||||
public String encode(int key1, int key2, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
setKey1(key1);
|
||||
setKey2(key2);
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
public String decode(int key1, int key2, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
setKey1(key1);
|
||||
setKey2(key2);
|
||||
setInputString(inputString);
|
||||
return decode();
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
key1 = 0;
|
||||
key2 = 0;
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public int getKey1(){
|
||||
return key1;
|
||||
}
|
||||
public int getKey2(){
|
||||
return key2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Atbash.java
|
||||
//Mattrixwv
|
||||
// Created: 07-25-21
|
||||
//Modified: 02-22-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
|
||||
|
||||
public class Atbash{
|
||||
private String inputString; //Holds the string that needs encoded or decoded
|
||||
private String outputString; //The encoded/decoded string
|
||||
private boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
private boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
private boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
//Encodes inputString and stores in outputString
|
||||
private String encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
//Step through every element in the inputString and shift it the correct amount
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
char currentChar = inputString.charAt(cnt);
|
||||
//Decode if the letter is alphabetic
|
||||
if(Character.isAlphabetic(currentChar)){
|
||||
//Use either uppercase or lowercase for the base
|
||||
//(letterbase + 25 - (currentChar - letterBase))
|
||||
if(Character.isUpperCase(currentChar)){
|
||||
output.append((char)(155 - currentChar));
|
||||
}
|
||||
else{
|
||||
output.append((char)(219 - currentChar));
|
||||
}
|
||||
}
|
||||
//Keep any punctuatio/whitespace the way it is
|
||||
else{
|
||||
output.append(currentChar);
|
||||
}
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Removes all invalid characters and sets inputString
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
//Convert all letters to lowercase
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
//Remove all characters except capital letters
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
//Remove all non-alpha numeric and whitespace symbols
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 character");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Atbash(){
|
||||
reset();
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
}
|
||||
public Atbash(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String encode(String inputString) throws InvalidInputException{
|
||||
//Make sure everything is empty before you begin
|
||||
reset();
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
public String decode(String inputString) throws InvalidInputException{
|
||||
return encode(inputString);
|
||||
}
|
||||
public void reset(){
|
||||
inputString = outputString = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Autokey.java
|
||||
//Mattrixwv
|
||||
// Created: 07-25-21
|
||||
//Modified: 07-03-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Autokey extends Vigenere{
|
||||
//Special rules for setting the strings for encoding
|
||||
private void encodeSet(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the input
|
||||
setInputString(inputString);
|
||||
|
||||
StringBuilder newKey = new StringBuilder();
|
||||
//Remove all unneccessary elements from the key
|
||||
setKeyword(keyword);
|
||||
newKey.append(keyword);
|
||||
//Remove all unneccessary elements from the input
|
||||
setKeyword(inputString);
|
||||
newKey.append(getKeyword());
|
||||
|
||||
//Make sure the key is not any longer than the input
|
||||
keyword = newKey.substring(0, getKeyword().length());
|
||||
|
||||
//Set the new keyword
|
||||
setKeyword(keyword);
|
||||
//Make sure to update the offset
|
||||
offset.clear();
|
||||
setOffset();
|
||||
}
|
||||
//Setting the strings for decoding
|
||||
private void decodeSet(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Remove all unneccessary elements from the key
|
||||
setKeyword(keyword);
|
||||
//Remove all unneccessary elements from the input
|
||||
setInputString(inputString);
|
||||
}
|
||||
//Decodes the inputString
|
||||
@Override
|
||||
protected String decode(){
|
||||
//Decode what the key will allow, add that to the key and continue
|
||||
StringBuilder currentOutput = new StringBuilder();
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and advance it the correct amount, according to offset
|
||||
int offsetCnt = 0;
|
||||
for(int letterCnt = 0;letterCnt < inputString.length();++letterCnt){
|
||||
//If we have reached the end of the keyword add what we have to it and continue
|
||||
if(offsetCnt == keyword.length()){
|
||||
setKeyword(keyword + currentOutput.toString());
|
||||
fullOutput.append(currentOutput);
|
||||
currentOutput = new StringBuilder();
|
||||
}
|
||||
|
||||
char letter = inputString.charAt(letterCnt);
|
||||
if(Character.isUpperCase(letter)){
|
||||
letter -= offset.get((offsetCnt++) % offset.size());
|
||||
if(letter < 'A'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'Z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
else if(Character.isLowerCase(letter)){
|
||||
letter -= offset.get((offsetCnt++) % offset.size());
|
||||
if(letter < 'a'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
currentOutput.append(letter);
|
||||
}
|
||||
//Empty the last character that were decoded
|
||||
fullOutput.append(currentOutput);
|
||||
|
||||
//Save and return the results
|
||||
outputString = fullOutput.toString();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Autokey(){
|
||||
super();
|
||||
}
|
||||
public Autokey(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
super(preserveCapitals, preserveWhitespace, preserveSymbols);
|
||||
}
|
||||
//Encodes inputString using the Autokey cipher
|
||||
@Override
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
reset();
|
||||
encodeSet(keyword, inputString);
|
||||
return encode();
|
||||
}
|
||||
//Decodes inputString using the Autokey cipher
|
||||
@Override
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
reset();
|
||||
decodeSet(keyword, inputString);
|
||||
return decode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
//CipherStreamJava/src/test/java/com/mattrixwv/CipherStreamJava/Baconian.java
|
||||
//Mattrixwv
|
||||
// Created: 01-12-22
|
||||
//Modified: 01-16-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
|
||||
public class Baconian{
|
||||
private static final ArrayList<String> code = new ArrayList<>(Arrays.asList(
|
||||
"aaaaa", "aaaab", "aaaba", "aaabb", "aabaa", "aabab", "aabba","aabbb", "abaaa", "abaaa", "abaab", "ababa", "ababb", //A-M
|
||||
"abbaa", "abbab", "abbba", "abbbb", "baaaa", "baaab", "baaba", "baabb", "baabb", "babaa", "babab", "babba", "babbb" //N-Z
|
||||
));
|
||||
private String inputString; //The string that needs encoded/decoded
|
||||
private String outputString; //The encoded/decoded string
|
||||
private boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
|
||||
//Sets the input string
|
||||
private void setInputStringEncode(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Remove all whitespace and symbols
|
||||
inputString = inputString.replaceAll("[^A-Za-z]", "");
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toLowerCase();
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
private void setInputStringDecode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toLowerCase();
|
||||
}
|
||||
|
||||
//Check each Baconian Cipher letter for validity
|
||||
for(String str : inputString.split(" ")){
|
||||
//Make sure each letter contains 5 characters
|
||||
if(str.length() != 5){
|
||||
throw new InvalidCharacterException("All Baconian letters contain exactly 5 characters: " + str);
|
||||
}
|
||||
//Make sure the letter contains only a's and b's
|
||||
String temp = str.replaceAll("[^abAB]", "");
|
||||
if(!temp.equals(str)){
|
||||
throw new InvalidCharacterException("Baconian letters contain only a's and b's: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input cannot be empty");
|
||||
}
|
||||
}
|
||||
//Encodes the inputString and stores the result in outputString
|
||||
private String encode(){
|
||||
StringJoiner output = new StringJoiner(" ");
|
||||
//Go through every character in the inputString and encode it
|
||||
for(char ch : inputString.toCharArray()){
|
||||
//Convert the character to a binary string with A = 0
|
||||
String binary = null;
|
||||
if(Character.isUpperCase(ch)){
|
||||
binary = code.get(ch - 'A').toUpperCase();
|
||||
}
|
||||
else{
|
||||
binary = code.get(ch - 'a').toLowerCase();
|
||||
}
|
||||
|
||||
//Add the encoded character to the output
|
||||
output.add(binary);
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes the inputString and stores the result in outputString
|
||||
private String decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
//Go through every Baconian Cipher character in the inputString and decode it
|
||||
for(String baconianCharacter : inputString.split(" ")){
|
||||
//Get the location of the Baconian character in the array
|
||||
int location = code.indexOf(baconianCharacter.toLowerCase());
|
||||
|
||||
//Convert the Baconian character to an ASCII character
|
||||
char ch;
|
||||
if(Character.isUpperCase(baconianCharacter.charAt(0))){
|
||||
ch = (char)(location + 'A');
|
||||
}
|
||||
else{
|
||||
ch = (char)(location + 'a');
|
||||
}
|
||||
|
||||
//Add the decoded character to the output
|
||||
output.append(ch);
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public Baconian(){
|
||||
reset();
|
||||
preserveCapitals = false;
|
||||
}
|
||||
public Baconian(boolean preserveCapitals){
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
}
|
||||
//Returns the outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Returns the inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Sets the inputString and encodes the message
|
||||
public String encode(String inputString) throws InvalidInputException{
|
||||
reset();
|
||||
setInputStringEncode(inputString);
|
||||
return encode();
|
||||
}
|
||||
//Sets the inputString and decodes the message
|
||||
public String decode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setInputStringDecode(inputString);
|
||||
return decode();
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/BaseX.java
|
||||
//Mattrixwv
|
||||
// Created: 01-08-22
|
||||
//Modified: 01-09-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidBaseException;
|
||||
|
||||
|
||||
public class BaseX{
|
||||
private String inputString; //The string that needs encoded/decoded
|
||||
private String outputString; //The encoded/decoded string
|
||||
private int base; //The base that the number will be encoded at
|
||||
|
||||
//Sets the input string
|
||||
private void setInputStringEncode(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
private void setInputStringDecode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
StringBuilder validNumbers = new StringBuilder();
|
||||
for(int cnt = 0;cnt < base;++cnt){
|
||||
validNumbers.append(Integer.toString(cnt, base).toUpperCase());
|
||||
}
|
||||
this.inputString = inputString.replaceAll("[^" + validNumbers.toString() + "\\s]", "");
|
||||
if(!this.inputString.equals(inputString)){
|
||||
throw new InvalidCharacterException("inputString cannot contain anything except numbers 0-" + Integer.toString(base - 1, base) + ", and whitespace");
|
||||
}
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Sets the numeric base
|
||||
private void setBase(int base) throws InvalidBaseException{
|
||||
if(base <= 0){
|
||||
throw new InvalidBaseException("Base cannot be a negative number");
|
||||
}
|
||||
|
||||
this.base = base;
|
||||
}
|
||||
//Encode inputString, store it in outputString, and return it
|
||||
private String encode(){
|
||||
//Encode every character in inputString
|
||||
StringJoiner output = new StringJoiner(" ");
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
//Get the next character
|
||||
char ch = inputString.charAt(cnt);
|
||||
//Encode the character to binary and add it to the output
|
||||
output.add(Integer.toString(ch, base));
|
||||
}
|
||||
|
||||
//Save the output
|
||||
outputString = output.toString().toUpperCase();
|
||||
|
||||
//Return the output
|
||||
return outputString;
|
||||
}
|
||||
//Decode inputString, store it in outputString, and return it
|
||||
private String decode() throws InvalidCharacterException{
|
||||
//Decode every binary number in the string
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(String baseXString : inputString.split(" ")){
|
||||
//Decode the current binary number
|
||||
int num = Integer.valueOf(baseXString, base);
|
||||
//Make sure it is in a valid range
|
||||
if((num < 0) && (num > 255)){
|
||||
throw new InvalidCharacterException("The base" + base + " string '" + baseXString + "' is not a valid character");
|
||||
}
|
||||
|
||||
//Convert the int to a char and save it
|
||||
output.append((char)num);
|
||||
}
|
||||
|
||||
//Save the output
|
||||
outputString = output.toString();
|
||||
|
||||
//Return the output
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public BaseX() throws InvalidBaseException{
|
||||
reset();
|
||||
setBase(2);
|
||||
}
|
||||
public BaseX(int base) throws InvalidBaseException{
|
||||
reset();
|
||||
setBase(base);
|
||||
}
|
||||
//Returns the inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns the outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Returns the base
|
||||
public int getBase(){
|
||||
return base;
|
||||
}
|
||||
//Sets the inputString and encodes the message
|
||||
public String encode(String inputString) throws InvalidInputException{
|
||||
reset();
|
||||
setInputStringEncode(inputString);
|
||||
return encode();
|
||||
}
|
||||
public String encode(int base, String inputString) throws InvalidBaseException, InvalidInputException{
|
||||
reset();
|
||||
setBase(base);
|
||||
setInputStringEncode(inputString);
|
||||
return encode();
|
||||
}
|
||||
//Sets the inputString and decodes the message
|
||||
public String decode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setInputStringDecode(inputString);
|
||||
return decode();
|
||||
}
|
||||
public String decode(int base, String inputString) throws InvalidBaseException, InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setBase(base);
|
||||
setInputStringDecode(inputString);
|
||||
return decode();
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Beaufort.java
|
||||
//Mattrixwv
|
||||
// Created: 02-23-22
|
||||
//Modified: 02-23-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Beaufort{
|
||||
private String inputString; //This is the string that needs encoded/decoded
|
||||
private String outputString; //This is the string that is output after encoding/decoding
|
||||
private String keyword; //This is the keyword that is responsible for determining the offsets that you change each character by
|
||||
private boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
private boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
private boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
private Atbash atbash; //The first step in encoding/decoding the cipher
|
||||
private Caesar caesar; //The second step in encoding/decoding the cipher
|
||||
private Vigenere vigenere; //The third step in encoding/decoding the cipher
|
||||
|
||||
//Ensures inputString constraints
|
||||
public void setInputString(String inputString) throws InvalidInputException{
|
||||
//Make sure the input isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Make sure the string isn't blank
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Ensures keyword constraints
|
||||
public void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
//Make sure the keyword isn't null
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Convert all letters to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
//Remove all characters except capital letters
|
||||
keyword = keyword.replaceAll("[^A-Z]", "");
|
||||
//Save the string
|
||||
this.keyword = keyword;
|
||||
|
||||
//If after all the elimination of unusable characters the keyword is empty throw an exception
|
||||
if(this.keyword.isBlank() || (this.keyword.length() < 2)){
|
||||
throw new InvalidKeywordException("Keyword must contain at least 2 letters");
|
||||
}
|
||||
}
|
||||
//Encodes the inputString and stores the result in outputString
|
||||
public void encode() throws InvalidKeywordException, InvalidInputException{
|
||||
//Reverse the string
|
||||
String atbashString = atbash.encode(inputString);
|
||||
//Shift the reversal by 1
|
||||
//?Not quite sure why this is needed. Need to look into this cipher a bit more closely
|
||||
String caesarString = caesar.encode(1, atbashString);
|
||||
//Shift each letter according to the key
|
||||
String vigenereString = vigenere.encode(keyword, caesarString);
|
||||
|
||||
//Save the output
|
||||
this.outputString = vigenereString;
|
||||
}
|
||||
//Decodes the inputString and stores the result in outputString
|
||||
public void decode() throws InvalidKeywordException, InvalidInputException{
|
||||
//Decoding is just encoding again
|
||||
encode();
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Beaufort(){
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
atbash = new Atbash(false, false, false);
|
||||
caesar = new Caesar(false, false, false);
|
||||
vigenere = new Vigenere(false, false, false);
|
||||
reset();
|
||||
}
|
||||
public Beaufort(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
atbash = new Atbash(preserveCapitals, preserveWhitespace, preserveSymbols);
|
||||
caesar = new Caesar(preserveCapitals, preserveWhitespace, preserveSymbols);
|
||||
vigenere = new Vigenere(preserveCapitals, preserveWhitespace, preserveSymbols);
|
||||
reset();
|
||||
}
|
||||
//Returns the current inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns the current outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Returns the current keyword
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
//Encodes inputString using keyword and returns the result
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Encode and return the message
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using keyword and returns the result
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Decode and return the message
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Caesar.java
|
||||
//Matthew Ellison
|
||||
// Created: 07-25-21
|
||||
//Modified: 02-17-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
|
||||
|
||||
public class Caesar{
|
||||
private String inputString; //The string that needs encoded/decoded
|
||||
private String outputString; //The encoded/decoded string
|
||||
private int shift; //The amount that you need to shift each letter
|
||||
private boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
private boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
private boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
//Sets shift and makes sure it is within the propper bounds
|
||||
private void setShift(int shiftAmount){
|
||||
//If you shift more than 26 you will just be wrapping back around again
|
||||
shift = shiftAmount % 26;
|
||||
}
|
||||
//Sets the input string
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toLowerCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Encodes the inputString and stores the result in outputString
|
||||
private String encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
char currentChar = inputString.charAt(cnt); //A temperary holder for the current working character
|
||||
//If it is an upper case letter shift it and wrap if necessary
|
||||
if(Character.isUpperCase(currentChar)){
|
||||
currentChar += shift;
|
||||
//Wrap around if the letter is now out of bounds
|
||||
if(currentChar < 'A'){
|
||||
currentChar += 26;
|
||||
}
|
||||
else if(currentChar > 'Z'){
|
||||
currentChar -= 26;
|
||||
}
|
||||
}
|
||||
//If it is a lower case letter shift it and wrap if necessary
|
||||
else if(Character.isLowerCase(currentChar)){
|
||||
currentChar += shift;
|
||||
//Wrap around if the letter is now out of bounds
|
||||
if(currentChar < 'a'){
|
||||
currentChar += 26;
|
||||
}
|
||||
else if(currentChar > 'z'){
|
||||
currentChar -= 26;
|
||||
}
|
||||
}
|
||||
//If it is whitespace, number, or punctuation just let it pass through
|
||||
//Add it to the output string
|
||||
output.append(currentChar);
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes the inputString and stores the result in outputString
|
||||
private String decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
char currentChar = inputString.charAt(cnt); //A temperary holder for the current working character
|
||||
//If it is an upper case letter shift it and wrap if necessary
|
||||
if(Character.isUpperCase(currentChar)){
|
||||
currentChar -= shift;
|
||||
//Wrap around if the letter is now out of bounds
|
||||
if(currentChar < 'A'){
|
||||
currentChar += 26;
|
||||
}
|
||||
else if(currentChar > 'Z'){
|
||||
currentChar -= 26;
|
||||
}
|
||||
}
|
||||
//If it is a lower case letter shift it and wrap if necessary
|
||||
else if(Character.isLowerCase(currentChar)){
|
||||
currentChar -= shift;
|
||||
//Wrap around if the letter is now out of bounds
|
||||
if(currentChar < 'a'){
|
||||
currentChar += 26;
|
||||
}
|
||||
else if(currentChar > 'z'){
|
||||
currentChar -= 26;
|
||||
}
|
||||
}
|
||||
//If it is whitespace, number, or punctuation just let it pass through
|
||||
//Add it to the output string
|
||||
output.append(currentChar);
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public Caesar(){
|
||||
reset();
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
}
|
||||
public Caesar(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
//Returns the inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns shift
|
||||
public int getShift(){
|
||||
return shift;
|
||||
}
|
||||
//Returns the outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Sets the shift and inputString and encodes the message
|
||||
public String encode(int shiftAmount, String inputString) throws InvalidInputException{
|
||||
reset();
|
||||
setShift(shiftAmount);
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
//Sets the shift and inputString and decodes the message
|
||||
public String decode(int shiftAmount, String inputString) throws InvalidInputException{
|
||||
reset();
|
||||
setShift(shiftAmount);
|
||||
setInputString(inputString);
|
||||
return decode();
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset(){
|
||||
inputString = outputString = "";
|
||||
shift = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/OneTimePad.java
|
||||
//Mattrixwv
|
||||
// Created: 02-23-22
|
||||
//Modified: 02-23-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class OneTimePad extends Vigenere{
|
||||
//?Add some kind of entropy calculator?
|
||||
//?Add some kind of "book passage includer"?
|
||||
|
||||
|
||||
//Constructor
|
||||
public OneTimePad(){
|
||||
super();
|
||||
}
|
||||
public OneTimePad(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
super(preserveCapitals, preserveWhitespace, preserveSymbols);
|
||||
}
|
||||
|
||||
//Encodes input using key and returns the result
|
||||
@Override
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
if(keyword.length() < inputString.length()){
|
||||
throw new InvalidKeywordException("Key must be at least as long as the input");
|
||||
}
|
||||
return super.encode(keyword, inputString);
|
||||
}
|
||||
//Decodes input using key and returns the result
|
||||
@Override
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
if(keyword.length() < inputString.length()){
|
||||
throw new InvalidKeywordException("Key must be at least as long as the input");
|
||||
}
|
||||
return super.decode(keyword, inputString);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Porta.java
|
||||
//Mattrixwv
|
||||
// Created: 02-28-22
|
||||
//Modified: 02-28-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Porta{
|
||||
private static final String[] tableau = {
|
||||
"NOPQRSTUVWXYZABCDEFGHIJKLM", //A-B
|
||||
"OPQRSTUVWXYZNMABCDEFGHIJKL", //C-D
|
||||
"PQRSTUVWXYZNOLMABCDEFGHIJK", //E-F
|
||||
"QRSTUVWXYZNOPKLMABCDEFGHIJ", //G-H
|
||||
"RSTUVWXYZNOPQJKLMABCDEFGHI", //I-J
|
||||
"STUVWXYZNOPQRIJKLMABCDEFGH", //K-L
|
||||
"TUVWXYZNOPQRSHIJKLMABCDEFG", //M-N
|
||||
"UVWXYZNOPQRSTGHIJKLMABCDEF", //O-P
|
||||
"VWXYZNOPQRSTUFGHIJKLMABCDE", //Q-R
|
||||
"WXYZNOPQRSTUVEFGHIJKLMABCD", //S-T
|
||||
"XYZNOPQRSTUVWDEFGHIJKLMABC", //U-V
|
||||
"YZNOPQRSTUVWXCDEFGHIJKLMAB", //W-X
|
||||
"ZNOPQRSTUVWXYBCDEFGHIJKLMA" //Y-Z
|
||||
};
|
||||
|
||||
private String inputString;
|
||||
private String outputString;
|
||||
private String keyword;
|
||||
private boolean preserveCapitals;
|
||||
private boolean preserveWhitespace;
|
||||
private boolean preserveSymbols;
|
||||
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
//Make sure the keyword isn't null
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Convert all letters to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
//Remove all characters except capital letters and save the string
|
||||
keyword = keyword.replaceAll("[^A-Z]", "");
|
||||
this.keyword = keyword;
|
||||
|
||||
//If after eliminating all ususable characters the keyword is empty throw an exception
|
||||
if(this.keyword.isBlank() || (this.keyword.length() < 2)){
|
||||
throw new InvalidKeywordException("Keyword must contain at least 2 letters");
|
||||
}
|
||||
}
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
//Ensure the input isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
private char getReplacer(int keywordCnt, char letter){
|
||||
char keyLetter = keyword.charAt(keywordCnt % keyword.length());
|
||||
int tableauColumn = (Character.toUpperCase(letter) - 'A');
|
||||
char replacer;
|
||||
|
||||
switch(keyLetter){
|
||||
case 'A':
|
||||
case 'B':
|
||||
replacer = tableau[0].charAt(tableauColumn); break;
|
||||
case 'C':
|
||||
case 'D':
|
||||
replacer = tableau[1].charAt(tableauColumn); break;
|
||||
case 'E':
|
||||
case 'F':
|
||||
replacer = tableau[2].charAt(tableauColumn); break;
|
||||
case 'G':
|
||||
case 'H':
|
||||
replacer = tableau[3].charAt(tableauColumn); break;
|
||||
case 'I':
|
||||
case 'J':
|
||||
replacer = tableau[4].charAt(tableauColumn); break;
|
||||
case 'K':
|
||||
case 'L':
|
||||
replacer = tableau[5].charAt(tableauColumn); break;
|
||||
case 'M':
|
||||
case 'N':
|
||||
replacer = tableau[6].charAt(tableauColumn); break;
|
||||
case 'O':
|
||||
case 'P':
|
||||
replacer = tableau[7].charAt(tableauColumn); break;
|
||||
case 'Q':
|
||||
case 'R':
|
||||
replacer = tableau[8].charAt(tableauColumn); break;
|
||||
case 'S':
|
||||
case 'T':
|
||||
replacer = tableau[9].charAt(tableauColumn); break;
|
||||
case 'U':
|
||||
case 'V':
|
||||
replacer = tableau[10].charAt(tableauColumn); break;
|
||||
case 'W':
|
||||
case 'X':
|
||||
replacer = tableau[11].charAt(tableauColumn); break;
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
replacer = tableau[12].charAt(tableauColumn); break;
|
||||
default:
|
||||
replacer = letter;
|
||||
}
|
||||
|
||||
return replacer;
|
||||
}
|
||||
private void encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and advance it the correct amount according to the keyword and tableau
|
||||
int keywordCnt = 0;
|
||||
for(char letter : inputString.toCharArray()){
|
||||
//If the character is a letter replace with the corresponding character from the tableau
|
||||
if(Character.isUpperCase(letter)){
|
||||
letter = Character.toUpperCase(getReplacer(keywordCnt, letter));
|
||||
++keywordCnt;
|
||||
}
|
||||
else if(Character.isLowerCase(letter)){
|
||||
letter = Character.toLowerCase(getReplacer(keywordCnt, letter));
|
||||
++keywordCnt;
|
||||
}
|
||||
|
||||
//Add the current character to the output
|
||||
output.append(letter);
|
||||
}
|
||||
|
||||
//Save the output
|
||||
outputString = output.toString();
|
||||
}
|
||||
private void decode(){
|
||||
//Decoding is the same as encoding
|
||||
encode();
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Porta(){
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
reset();
|
||||
}
|
||||
public Porta(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
reset();
|
||||
}
|
||||
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Encode and return the message
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Decode and return the message
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Substitution.java
|
||||
//Mattrixwv
|
||||
// Created: 02-22-22
|
||||
//Modified: 02-22-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Substitution{
|
||||
private String inputString;
|
||||
private String outputString;
|
||||
private String key;
|
||||
private boolean preserveCapitals;
|
||||
private boolean preserveWhitespace;
|
||||
private boolean preserveSymbols;
|
||||
|
||||
private void setKey(String key) throws InvalidKeywordException{
|
||||
if(key == null){
|
||||
throw new InvalidKeywordException("Key cannot be null");
|
||||
}
|
||||
|
||||
//Transform all letters to uppercase
|
||||
key = key.toUpperCase();
|
||||
|
||||
//Make sure the key contains no duplicate mappings
|
||||
String tempKey = key.replaceAll("(.)\\1{2}", "");
|
||||
if(!tempKey.equals(key)){
|
||||
throw new InvalidKeywordException("The key cannot contain duplicate mappings");
|
||||
}
|
||||
|
||||
//Make sure the key is a valid length
|
||||
if(key.length() == 26){
|
||||
//Make sure the key contains all valid characters
|
||||
tempKey = key.replaceAll("[^A-Z]", "");
|
||||
if(!tempKey.equals(key)){
|
||||
throw new InvalidKeywordException("The key must contain all letters");
|
||||
}
|
||||
}
|
||||
else if(key.length() == 36){
|
||||
//Make sure the key contains all valid characters
|
||||
tempKey = key.replaceAll("[^A-Z0-9]", "");
|
||||
if(!tempKey.equals(key)){
|
||||
throw new InvalidKeywordException("The key must contain all letters and can contain all numbers");
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new InvalidKeywordException("The key must contain all letters and can contain all numbers");
|
||||
}
|
||||
|
||||
//Save the key
|
||||
this.key = key;
|
||||
}
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Remove any data that should not be preserved
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the inputString
|
||||
this.inputString = inputString;
|
||||
|
||||
//Make sure there is still input
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
private void encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and convert it
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(key.charAt(ch - 'A')));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(key.charAt(ch - 'a')));
|
||||
}
|
||||
else if(Character.isDigit(ch) && (key.length() == 36)){
|
||||
output.append(key.charAt('Z' - 'A' + Integer.valueOf(Character.toString(ch)) + 1));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
this.outputString = output.toString();
|
||||
}
|
||||
private void decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and convert it
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append((char)('A' + key.indexOf(Character.toUpperCase(ch))));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append((char)('a' + key.indexOf(Character.toUpperCase(ch))));
|
||||
}
|
||||
else if(Character.isDigit(ch) && (key.length() == 36)){
|
||||
output.append((char)('0' + (key.indexOf(Character.toUpperCase(ch)) - 26)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
this.outputString = output.toString();
|
||||
}
|
||||
|
||||
public Substitution(){
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
reset();
|
||||
}
|
||||
public Substitution(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
reset();
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return key;
|
||||
}
|
||||
public String encode(String key, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
setKey(key);
|
||||
setInputString(inputString);
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
public String decode(String key, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
setKey(key);
|
||||
setInputString(inputString);
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
key = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/monoSubstitution/Vigenere.java
|
||||
//Matthew Ellison
|
||||
// Created: 07-25-21
|
||||
//Modified: 02-22-22
|
||||
package com.mattrixwv.cipherstream.monosubstitution;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Vigenere{
|
||||
protected String inputString; //This is the string that needs encoded/decoded
|
||||
protected String outputString; //This is the string that is output after encoding/decoding
|
||||
protected String keyword; //This is the keyword that is resposible for determining the offsets that you change each character by
|
||||
protected ArrayList<Integer> offset; //Holds the offsets coputed from each character in the keyword
|
||||
protected boolean preserveCapitals; //Whether to respect capitals in the output string
|
||||
protected boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
protected boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
|
||||
//Uses keyword to calculate the offset for the Caesar cipher for each character
|
||||
protected void setOffset(){
|
||||
//Reserve the correct size to increase speed later
|
||||
offset.ensureCapacity(keyword.length());
|
||||
|
||||
//Loop through every letter in keyword and get the offset from A
|
||||
for(int cnt = 0;cnt < keyword.length();++cnt){
|
||||
char letter = keyword.charAt(cnt);
|
||||
offset.add((letter - 'A') % 26);
|
||||
}
|
||||
}
|
||||
//Sets inputString
|
||||
protected void setInputString(String inputString) throws InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Sets keyword
|
||||
protected void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
if(keyword == null){
|
||||
throw new NullPointerException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Convert all letters to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
//Remove all characters except capital letters
|
||||
keyword = keyword.replaceAll("[^A-Z]", "");
|
||||
//Save the string
|
||||
this.keyword = keyword;
|
||||
|
||||
//Make sure offset is empty before adding to it
|
||||
offset.clear();
|
||||
setOffset();
|
||||
|
||||
//If after all the eliminating of unusable characters the keyword is empty throw an exception
|
||||
if(this.keyword.isBlank()){
|
||||
throw new InvalidKeywordException("Keyword must contain at least 2 letters");
|
||||
}
|
||||
}
|
||||
//Encodes inputString and stores the result in outputString
|
||||
protected String encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and advance it the correct amount, according to offset
|
||||
int offsetCnt = 0;
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
char letter = inputString.charAt(inputCnt);
|
||||
if(Character.isUpperCase(letter)){
|
||||
letter += offset.get((offsetCnt++) % offset.size());
|
||||
//Make sure the character is still a letter, if not, wrap around
|
||||
if(letter < 'A'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'Z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
else if(Character.isLowerCase(letter)){
|
||||
letter += offset.get((offsetCnt++) % offset.size());
|
||||
//Make sure the character is still a letter, if not, wrap around
|
||||
if(letter < 'a'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
output.append(letter);
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString and stores the result in outputString
|
||||
protected String decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
//Step through every character in the inputString and advance it the correct amount, according to offset
|
||||
int offsetCnt = 0;
|
||||
for(int letterCnt = 0;letterCnt < inputString.length();++letterCnt){
|
||||
char letter = inputString.charAt(letterCnt);
|
||||
if(Character.isUpperCase(letter)){
|
||||
letter -= offset.get((offsetCnt++) % offset.size());
|
||||
if(letter < 'A'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'Z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
else if(Character.isLowerCase(letter)){
|
||||
letter -= offset.get((offsetCnt++) % offset.size());
|
||||
if(letter < 'a'){
|
||||
letter += 26;
|
||||
}
|
||||
else if(letter > 'z'){
|
||||
letter -= 26;
|
||||
}
|
||||
}
|
||||
output.append(letter);
|
||||
}
|
||||
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Vigenere(){
|
||||
offset = new ArrayList<>();
|
||||
reset();
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
}
|
||||
public Vigenere(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
offset = new ArrayList<>();
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
//Returns the current inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns the current outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Returns the current keyword
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
//Returns the current offsets (Used mostly in bug fixing)
|
||||
public List<Integer> getOffsets(){
|
||||
return offset;
|
||||
}
|
||||
//Encodes input using key and returns the result
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
//Decodes input using key and returns the result
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
return decode();
|
||||
}
|
||||
//Makes sure all of the variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
offset.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Bifid.java
|
||||
//Mattrixwv
|
||||
// Created: 03-03-22
|
||||
//Modified: 03-03-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
public class Bifid{
|
||||
private String inputString; //The message that needs to be encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
private String keyword; //The keyword used to create the grid
|
||||
private PolybiusSquare polybiusSquare; //Used to encode the message to numbers then back into letters
|
||||
private boolean preserveCapitals; //Persist capitals in the output string
|
||||
private boolean preserveWhitespace; //Persist whitespace in the output string
|
||||
private boolean preserveSymbols; //Persist symbols in the output string
|
||||
|
||||
//Strips invalid characters from the keyword and creates the grid
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
//Ensure the keyword isn't null
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Save the key for polybius to deal with
|
||||
this.keyword = keyword;
|
||||
}
|
||||
//Ensures inputString constraints
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
//Ensure the input string isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Adds all non-letter characters back to the output string
|
||||
private void formatOutput(String outputString){
|
||||
//Keep track of where you are in the output
|
||||
int outputCnt = 0;
|
||||
StringBuilder output = new StringBuilder();
|
||||
//Check every character in the input and apply the correct rules to the output
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
//Save the output
|
||||
this.outputString = output.toString();
|
||||
}
|
||||
//Encodes inputString using a polybius square and stores the result in outputString
|
||||
private void encode() throws InvalidCharacterException, InvalidInputException{
|
||||
//Get the encoded numbers from a polybius square
|
||||
String numberResult = polybiusSquare.encode(keyword, inputString).replaceAll("\\s", "");
|
||||
keyword = polybiusSquare.getKeyword(); //Save the cleaned keyword
|
||||
|
||||
//Split the numbers into 2 rows and rejoin the rows to create a new string
|
||||
StringBuilder row0 = new StringBuilder();
|
||||
StringBuilder row1 = new StringBuilder();
|
||||
boolean firstNum = true;
|
||||
for(char ch : numberResult.toCharArray()){
|
||||
if(firstNum){
|
||||
row0.append(ch);
|
||||
}
|
||||
else{
|
||||
row1.append(ch);
|
||||
}
|
||||
firstNum = !firstNum;
|
||||
}
|
||||
String shuffledResult = row0.toString() + row1.toString();
|
||||
|
||||
//Take the new string and decode the numbers using polybius
|
||||
String letterResult = polybiusSquare.decode(keyword, shuffledResult);
|
||||
|
||||
//Format the output
|
||||
formatOutput(letterResult);
|
||||
}
|
||||
//Decodes inputString using a polybius square and stores the result in outputString
|
||||
private void decode() throws InvalidCharacterException, InvalidInputException{
|
||||
//Get the decoded number from a polybius square
|
||||
String numberResult = polybiusSquare.encode(keyword, inputString).replaceAll("\\s", "");
|
||||
keyword = polybiusSquare.getKeyword();
|
||||
|
||||
//Split the numbers into to rows and rejoin the rows to create a new string
|
||||
String row0 = numberResult.substring(0, numberResult.length() / 2);
|
||||
String row1 = numberResult.substring(numberResult.length() / 2);
|
||||
StringBuilder unshuffledResult = new StringBuilder();
|
||||
for(int cnt = 0;cnt < row0.length();++cnt){
|
||||
unshuffledResult.append(row0.charAt(cnt));
|
||||
unshuffledResult.append(row1.charAt(cnt));
|
||||
}
|
||||
|
||||
//Take the new string and decode the numbers using polybius
|
||||
String letterResult = polybiusSquare.decode(keyword, unshuffledResult.toString());
|
||||
|
||||
//Format the outputString
|
||||
formatOutput(letterResult);
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Bifid() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
polybiusSquare = new PolybiusSquare(false, false);
|
||||
reset();
|
||||
}
|
||||
public Bifid(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
polybiusSquare = new PolybiusSquare(false, false);
|
||||
reset();
|
||||
}
|
||||
//Encodes inputString using keyword and returns the result
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Encode and return the message
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using keyword and returns the result
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(inputString);
|
||||
|
||||
//Decode and return the message
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
//Gets
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
//MattrixwvWebsite/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Columnar.java
|
||||
//Mattrixwv
|
||||
// Created: 01-16-22
|
||||
//Modified: 03-03-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
|
||||
|
||||
public class Columnar{
|
||||
private String inputString; //The message that needs to be encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
private String keyword; //The keyword used to create the grid
|
||||
private char characterToAdd; //The character that is added to the end of a string to bring it to the correct length
|
||||
private int charsAdded; //The number of characters that were added to the end of the message
|
||||
private ArrayList<ArrayList<Character>> grid; //The grid used to encode/decode the message
|
||||
private boolean preserveCapitals; //Persist capitals in the output string
|
||||
private boolean preserveWhitespace; //Persist whitespace in the output string
|
||||
private boolean preserveSymbols; //Persist symbols in the output string
|
||||
private boolean removePadding; //Remove the padding letters added to the cipher
|
||||
|
||||
//Strip the inputString of all non-letter characters and change them to capitals
|
||||
private String getCleanInputString(){
|
||||
return inputString.toUpperCase().replaceAll("[^A-Z]", "");
|
||||
}
|
||||
//Create the grid from the keyword
|
||||
private void createGridEncode(){
|
||||
//Add the keyword to the first row in the array
|
||||
grid = new ArrayList<>();
|
||||
grid.add(new ArrayList<>(Arrays.asList(keyword.chars().mapToObj(c -> (char)c).toArray(Character[]::new))));
|
||||
|
||||
//Create the input that will be used for encoding
|
||||
String gridInputString = getCleanInputString();
|
||||
|
||||
//Create arrays from the inputString of keyword.size length and add them each as new rows to the grid
|
||||
//?gridRow could be changed to grid.size(), but would it be worth the extra confusion? Probably not unless I really want to min/max
|
||||
for(int gridRow = 1;(keyword.length() * gridRow) <= gridInputString.length();++gridRow){
|
||||
grid.add(new ArrayList<>(Arrays.asList(
|
||||
gridInputString.substring(keyword.length() * (gridRow - 1), (keyword.length() * gridRow))
|
||||
.chars().mapToObj(c->(char)c).toArray(Character[]::new)
|
||||
)));
|
||||
}
|
||||
}
|
||||
private void createGridDecode(){
|
||||
//Add the keyword to the first row in the array
|
||||
grid = new ArrayList<>();
|
||||
StringBuilder orderedKeyword = new StringBuilder();
|
||||
for(int cnt : getKeywordAlphaLocations()){
|
||||
orderedKeyword.append(keyword.charAt(cnt));
|
||||
}
|
||||
grid.add(new ArrayList<>(Arrays.asList(orderedKeyword.chars().mapToObj(c -> (char)c).toArray(Character[]::new))));
|
||||
|
||||
//Create the input that will be used for encoding
|
||||
String gridInputString = getCleanInputString();
|
||||
|
||||
//Make sure the grid has the appropritate number of rows
|
||||
int numRows = inputString.length() / keyword.length();
|
||||
while(grid.size() <= numRows){
|
||||
grid.add(new ArrayList<>());
|
||||
}
|
||||
//Add each character to the grid
|
||||
int rowCnt = 1;
|
||||
for(char ch : gridInputString.toCharArray()){
|
||||
grid.get(rowCnt++).add(ch);
|
||||
if(rowCnt > numRows){
|
||||
rowCnt = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Strips invalid characters from the string that needs encoded/decoded
|
||||
private void setInputStringEncode(String inputString) throws InvalidInputException{
|
||||
//Ensure the input isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input must not be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Make sure the input is the correct length
|
||||
this.inputString = inputString;
|
||||
StringBuilder inputStringBuilder = new StringBuilder();
|
||||
inputStringBuilder.append(inputString);
|
||||
int cleanLength = getCleanInputString().length();
|
||||
int charsToAdd = (cleanLength % keyword.length());
|
||||
if(charsToAdd != 0){
|
||||
charsToAdd = keyword.length() - charsToAdd;
|
||||
}
|
||||
for(int cnt = 0;cnt < charsToAdd;++cnt){
|
||||
inputStringBuilder.append(characterToAdd);
|
||||
}
|
||||
charsAdded = charsToAdd;
|
||||
inputString = inputStringBuilder.toString();
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank() || getCleanInputString().isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
private void setInputStringDecode(String inputString) throws InvalidInputException{
|
||||
//Ensure the input isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input must not be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("[\\s]", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Make sure the input is the correct length
|
||||
charsAdded = 0;
|
||||
this.inputString = inputString;
|
||||
//Figure out how many rows there will be
|
||||
int numRows = getCleanInputString().length() / keyword.length();
|
||||
//Get the number of characters the input is over the limit
|
||||
int numCharsOver = getCleanInputString().length() % keyword.length();
|
||||
if(numCharsOver > 0){
|
||||
//Get the first n of the characters in the keyword and their encoded column locations
|
||||
ArrayList<Integer> originalLocations = getKeywordOriginalLocations();
|
||||
ArrayList<Integer> longColumns = new ArrayList<>();
|
||||
for(int cnt = 0;cnt < numCharsOver;++cnt){
|
||||
longColumns.add(originalLocations.get(cnt));
|
||||
}
|
||||
|
||||
//Add letters where they need to be added to fill out the grid
|
||||
StringBuilder input = new StringBuilder();
|
||||
int col = 0;
|
||||
int row = 0;
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(!Character.isAlphabetic(ch)){
|
||||
input.append(ch);
|
||||
continue;
|
||||
}
|
||||
if(row < numRows){
|
||||
input.append(ch);
|
||||
++row;
|
||||
}
|
||||
else{
|
||||
if(longColumns.contains(col)){
|
||||
input.append(ch);
|
||||
row = 0;
|
||||
}
|
||||
else{
|
||||
input.append(characterToAdd);
|
||||
++charsAdded;
|
||||
input.append(ch);
|
||||
row = 1;
|
||||
}
|
||||
++col;
|
||||
}
|
||||
}
|
||||
if(!longColumns.contains(col)){
|
||||
input.append(characterToAdd);
|
||||
++charsAdded;
|
||||
}
|
||||
inputString = input.toString();
|
||||
}
|
||||
|
||||
//Save the input
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank() || getCleanInputString().isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
//Creates the output string from the grid
|
||||
private void createOutputStringFromColumns(){
|
||||
//Get the current rows of any characters that you added
|
||||
ArrayList<Integer> colsAddedTo = new ArrayList<>();
|
||||
if(removePadding){
|
||||
ArrayList<Integer> cols = getKeywordOriginalLocations();
|
||||
Collections.reverse(cols);
|
||||
for(int cnt = 0;cnt < charsAdded;++cnt){
|
||||
colsAddedTo.add(cols.get(cnt));
|
||||
}
|
||||
}
|
||||
else{
|
||||
charsAdded = 0;
|
||||
}
|
||||
|
||||
//Turn the grid into a string
|
||||
StringBuilder gridOutput = new StringBuilder();
|
||||
for(int col = 0;col < grid.get(0).size();++col){
|
||||
for(int row = 1;row < grid.size();++row){
|
||||
gridOutput.append(grid.get(row).get(col));
|
||||
}
|
||||
if(colsAddedTo.contains(col)){
|
||||
gridOutput.deleteCharAt(gridOutput.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
//Preserve any remaining symbols in the string
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(int outputLoc = 0, inputLoc = 0;inputLoc < (inputString.length() - charsAdded);){
|
||||
char inputChar = inputString.charAt(inputLoc++);
|
||||
if(Character.isUpperCase(inputChar)){
|
||||
output.append(Character.toUpperCase(gridOutput.charAt(outputLoc++)));
|
||||
}
|
||||
else if(Character.isLowerCase(inputChar)){
|
||||
output.append(Character.toLowerCase(gridOutput.charAt(outputLoc++)));
|
||||
}
|
||||
else{
|
||||
output.append(inputChar);
|
||||
}
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
}
|
||||
private void createOutputStringFromRows(){
|
||||
//Turn the grid into a string
|
||||
StringBuilder gridOutput = new StringBuilder();
|
||||
for(int row = 1;row < grid.size();++row){
|
||||
for(int col = 0;col < grid.get(row).size();++col){
|
||||
gridOutput.append(grid.get(row).get(col));
|
||||
}
|
||||
}
|
||||
//Remove any added characters
|
||||
if(removePadding){
|
||||
for(int cnt = 0;cnt < charsAdded;++cnt){
|
||||
gridOutput.deleteCharAt(gridOutput.length() - 1);
|
||||
}
|
||||
}
|
||||
else{
|
||||
charsAdded = 0;
|
||||
}
|
||||
|
||||
ArrayList<Integer> colsAddedTo = new ArrayList<>();
|
||||
if(removePadding){
|
||||
ArrayList<Integer> cols = getKeywordOriginalLocations();
|
||||
Collections.reverse(cols);
|
||||
for(int cnt = 0;cnt < charsAdded;++cnt){
|
||||
colsAddedTo.add(cols.get(cnt));
|
||||
}
|
||||
}
|
||||
//Preserve any remaining symbols in the string
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(int outputLoc = 0, inputLoc = 0, row = 1, col = 0;inputLoc < inputString.length();){
|
||||
char inputChar = inputString.charAt(inputLoc++);
|
||||
if(((row + 1) == grid.size()) && (colsAddedTo.contains(col)) && (Character.isAlphabetic(inputChar))){
|
||||
++col;
|
||||
row = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(Character.isUpperCase(inputChar)){
|
||||
output.append(Character.toUpperCase(gridOutput.charAt(outputLoc++)));
|
||||
++row;
|
||||
}
|
||||
else if(Character.isLowerCase(inputChar)){
|
||||
output.append(Character.toLowerCase(gridOutput.charAt(outputLoc++)));
|
||||
++row;
|
||||
}
|
||||
else{
|
||||
output.append(inputChar);
|
||||
}
|
||||
|
||||
if(row == grid.size()){
|
||||
++col;
|
||||
row = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
}
|
||||
//Strips invalid characters from the keyword and creates the grid
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
//Ensure the keyword isn't null
|
||||
if(keyword == null){
|
||||
throw new NullPointerException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Strip all non-letter characters and change them to uppercase
|
||||
this.keyword = keyword.toUpperCase().replaceAll("[^A-Z]", "");
|
||||
|
||||
//Make sure a valid keyword is present
|
||||
if(this.keyword.length() < 2){
|
||||
throw new InvalidKeywordException("The keyword must contain at least 2 letters");
|
||||
}
|
||||
}
|
||||
//Set the character that is added to the end of the string
|
||||
private void setCharacterToAdd(char characterToAdd) throws InvalidCharacterException{
|
||||
if(!Character.isAlphabetic(characterToAdd)){
|
||||
throw new InvalidCharacterException("Character to add must be a letter");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
this.characterToAdd = Character.toUpperCase(characterToAdd);
|
||||
}
|
||||
else{
|
||||
this.characterToAdd = characterToAdd;
|
||||
}
|
||||
}
|
||||
//Returns a list of integers that represents the location of the characters of the keyword in alphabetic order
|
||||
private ArrayList<Integer> getKeywordAlphaLocations(){
|
||||
ArrayList<Integer> orderedLocations = new ArrayList<>();
|
||||
//go through every letter and check it against the keyword
|
||||
for(char ch = 'A';ch <= 'Z';++ch){
|
||||
for(int cnt = 0;cnt < keyword.length();++cnt){
|
||||
//If the current letter is the same as the letter we are looking for add the location to the array
|
||||
if(keyword.charAt(cnt) == ch){
|
||||
orderedLocations.add(cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Return the alphabetic locations
|
||||
return orderedLocations;
|
||||
}
|
||||
private ArrayList<Integer> getKeywordOriginalLocations(){
|
||||
//Figure out the order the columns are in
|
||||
ArrayList<Integer> orderedLocations = getKeywordAlphaLocations();
|
||||
//Figure out what order the columns need rearanged to
|
||||
ArrayList<Integer> originalOrder = new ArrayList<>();
|
||||
for(int orgCnt = 0;orgCnt < orderedLocations.size();++orgCnt){
|
||||
for(int orderedCnt = 0;orderedCnt < orderedLocations.size();++orderedCnt){
|
||||
if(orderedLocations.get(orderedCnt) == orgCnt){
|
||||
originalOrder.add(orderedCnt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return originalOrder;
|
||||
}
|
||||
//Rearanges the grid based on the list of numbers given
|
||||
private void rearangeGrid(ArrayList<Integer> listOrder){
|
||||
//Create a new grid and make sure it is the same size as the original grid
|
||||
int numCol = grid.get(0).size();
|
||||
ArrayList<ArrayList<Character>> newGrid = new ArrayList<>(grid.size());
|
||||
for(int cnt = 0;cnt < grid.size();++cnt){
|
||||
newGrid.add(new ArrayList<>(numCol));
|
||||
}
|
||||
|
||||
//Step through the list order, pull out the columns, and add them to the new grid
|
||||
for(int col : listOrder){
|
||||
for(int row = 0;row < grid.size();++row){
|
||||
newGrid.get(row).add(grid.get(row).get(col));
|
||||
}
|
||||
}
|
||||
|
||||
//Save the new grid
|
||||
grid = newGrid;
|
||||
}
|
||||
//Encodes inputString using the Columnar cipher and stores the result in outputString
|
||||
private void encode(){
|
||||
//Create the grid
|
||||
createGridEncode();
|
||||
//Figure out the new column order
|
||||
ArrayList<Integer> orderedLocations = getKeywordAlphaLocations();
|
||||
//Rearange the grid to the new order
|
||||
rearangeGrid(orderedLocations);
|
||||
//Create the output
|
||||
createOutputStringFromColumns();
|
||||
}
|
||||
//Decodes inputString using the Columnar cipher and stores the result in outputString
|
||||
private void decode(){
|
||||
//Create the grid
|
||||
createGridDecode();
|
||||
ArrayList<Integer> originalOrder = getKeywordOriginalLocations();
|
||||
//Rearange the grid to the original order
|
||||
rearangeGrid(originalOrder);
|
||||
//Create the output
|
||||
createOutputStringFromRows();
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public Columnar() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
removePadding = false;
|
||||
setCharacterToAdd('x');
|
||||
reset();
|
||||
}
|
||||
public Columnar(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, boolean removePadding) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
this.removePadding = removePadding;
|
||||
setCharacterToAdd('x');
|
||||
reset();
|
||||
}
|
||||
public Columnar(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, boolean removePadding, char characterToAdd) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
this.removePadding = removePadding;
|
||||
setCharacterToAdd(characterToAdd);
|
||||
reset();
|
||||
}
|
||||
//Encodes inputString using keyword and returns the result
|
||||
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputStringEncode(inputString);
|
||||
|
||||
//Encode and return the message
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Encodes inputString using keyword and returns the result
|
||||
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputStringDecode(inputString);
|
||||
|
||||
//Decode and return the message
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
grid = new ArrayList<>();
|
||||
charsAdded = 0;
|
||||
}
|
||||
//Gets
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java
|
||||
//Mattrixwv
|
||||
// Created: 01-31-22
|
||||
//Modified: 02-17-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.matrix.ModMatrix;
|
||||
import com.mattrixwv.matrix.exceptions.InvalidGeometryException;
|
||||
import com.mattrixwv.matrix.exceptions.InvalidScalarException;
|
||||
|
||||
|
||||
public class Hill{
|
||||
private boolean preserveCapitals;
|
||||
private boolean preserveWhitespace;
|
||||
private boolean preserveSymbols;
|
||||
private boolean removePadding;
|
||||
private String inputString;
|
||||
private String outputString;
|
||||
private char characterToAdd;
|
||||
private int charsAdded;
|
||||
private ModMatrix key;
|
||||
|
||||
private void setKey(ModMatrix key) throws InvalidKeyException{
|
||||
//Make sure the mod is correct
|
||||
if(key.getMod() != 26){
|
||||
throw new InvalidKeyException("This algorithm uses the english alphabet, so the mod for the key must be 26");
|
||||
}
|
||||
|
||||
//Make sure the matrix is square
|
||||
if(!key.isSquare()){
|
||||
throw new InvalidKeyException("The key must be a square matrix");
|
||||
}
|
||||
|
||||
//Make sure the matrix is invertable
|
||||
try{
|
||||
key.inverse();
|
||||
}
|
||||
catch(InvalidGeometryException | InvalidScalarException error){
|
||||
throw new InvalidKeyException("The key does not have an inverse mod 26");
|
||||
}
|
||||
|
||||
//Set the key
|
||||
this.key = new ModMatrix(key);
|
||||
}
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
//Remove anything that needs removed
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input must not be null");
|
||||
}
|
||||
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("[\\s]", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Make sure the input is correct length
|
||||
this.inputString = inputString;
|
||||
int cleanLength = getCleanInputString().length();
|
||||
int charsToAdd = (cleanLength % key.getNumRows());
|
||||
StringBuilder inputStringBuilder = new StringBuilder();
|
||||
inputStringBuilder.append(inputString);
|
||||
if(charsToAdd != 0){
|
||||
charsToAdd = key.getNumRows() - charsToAdd;
|
||||
}
|
||||
for(int cnt = 0;cnt < charsToAdd;++cnt){
|
||||
inputStringBuilder.append(characterToAdd);
|
||||
}
|
||||
charsAdded = charsToAdd;
|
||||
inputString = inputStringBuilder.toString();
|
||||
|
||||
this.inputString = inputString;
|
||||
|
||||
//Make sure the input isn't blank
|
||||
if(this.inputString.isBlank() || getCleanInputString().isBlank()){
|
||||
throw new InvalidInputException("Input cannot be blank");
|
||||
}
|
||||
}
|
||||
private String getCleanInputString(){
|
||||
return inputString.toUpperCase().replaceAll("[^A-Z]", "");
|
||||
}
|
||||
private void setCharacterToAdd(char characterToAdd) throws InvalidCharacterException{
|
||||
//Make sure the character is a letter
|
||||
if(!Character.isAlphabetic(characterToAdd)){
|
||||
throw new InvalidCharacterException("Character to add must be a letter");
|
||||
}
|
||||
|
||||
//Save the characterToAdd
|
||||
if(!preserveCapitals){
|
||||
this.characterToAdd = Character.toUpperCase(characterToAdd);
|
||||
}
|
||||
else{
|
||||
this.characterToAdd = characterToAdd;
|
||||
}
|
||||
}
|
||||
private String polishOutputString(){
|
||||
//Add the extra characters back to the output and remove the added characters
|
||||
int outputCnt = 0;
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
outputBuilder.append(Character.toUpperCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
outputBuilder.append(Character.toLowerCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else{
|
||||
outputBuilder.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if(removePadding){
|
||||
String tempString = outputBuilder.substring(0, outputBuilder.length() - charsAdded);
|
||||
outputBuilder = new StringBuilder();
|
||||
outputBuilder.append(tempString);
|
||||
}
|
||||
|
||||
return outputBuilder.toString();
|
||||
}
|
||||
private ArrayList<ModMatrix> getInputVectors(){
|
||||
//Get the number of columns in the key
|
||||
int numCols = key.getNumCols();
|
||||
|
||||
//Get a clean inputString
|
||||
String cleanInput = getCleanInputString();
|
||||
|
||||
//Break the inputString up into lengths of numCols
|
||||
ArrayList<ModMatrix> vectors = new ArrayList<>();
|
||||
for(int cnt = 0;cnt < cleanInput.length();cnt += numCols){
|
||||
String subString = cleanInput.substring(cnt, cnt + numCols);
|
||||
int[] grid = new int[numCols];
|
||||
|
||||
//Subtract 65 from each character so that A=0, B=1, ...
|
||||
for(int subCnt = 0;subCnt < subString.length();++subCnt){
|
||||
grid[subCnt] = subString.charAt(subCnt) - 65;
|
||||
}
|
||||
|
||||
//Create a vector from the new values
|
||||
ModMatrix vector = new ModMatrix(26);
|
||||
vector.addCol(grid);
|
||||
|
||||
//Add the vector to the array
|
||||
vectors.add(vector);
|
||||
}
|
||||
|
||||
//Return the array of vectors
|
||||
return vectors;
|
||||
}
|
||||
private String getOutputFromVectors(ArrayList<ModMatrix> outputVectors){
|
||||
//Go through each element in the vector
|
||||
StringBuilder outputBuilder = new StringBuilder();
|
||||
for(ModMatrix vector : outputVectors){
|
||||
//Add 65 to each element and add it to the string
|
||||
for(int cnt = 0;cnt < vector.getNumRows();++cnt){
|
||||
outputBuilder.append((char)(vector.get(cnt, 0) + 65));
|
||||
}
|
||||
}
|
||||
|
||||
//Return the new string
|
||||
return outputBuilder.toString();
|
||||
}
|
||||
private String encode(){
|
||||
//Get an array of vectors that we are going to encode
|
||||
ArrayList<ModMatrix> inputVectors = getInputVectors();
|
||||
|
||||
//Multiply the key by each vector and add the result to a new vector
|
||||
ArrayList<ModMatrix> outputVectors = new ArrayList<>();
|
||||
for(ModMatrix inputVector : inputVectors){
|
||||
ModMatrix outputVector = key.multiply(inputVector);
|
||||
outputVectors.add(outputVector);
|
||||
}
|
||||
|
||||
//Take the array of results and turn them back into letters
|
||||
outputString = getOutputFromVectors(outputVectors);
|
||||
|
||||
//Add the extra characters back to the output and remove the added characters
|
||||
outputString = polishOutputString();
|
||||
|
||||
//Save the output
|
||||
return outputString;
|
||||
}
|
||||
private String decode(){
|
||||
//Get the array of vectors that we are going to decode
|
||||
ArrayList<ModMatrix> inputVectors = getInputVectors();
|
||||
|
||||
//Multiply the inverse of the key by each vector and add the result to a new vector
|
||||
ModMatrix inverseKey = key.inverse();
|
||||
ArrayList<ModMatrix> outputVectors = new ArrayList<>();
|
||||
for(ModMatrix inputVector : inputVectors){
|
||||
ModMatrix outputVector = inverseKey.multiply(inputVector);
|
||||
outputVectors.add(outputVector);
|
||||
}
|
||||
|
||||
//Take the array of results and turn them back into letters
|
||||
outputString = getOutputFromVectors(outputVectors);
|
||||
|
||||
//Add the extra characters back to the output and remove the added characters
|
||||
outputString = polishOutputString();
|
||||
|
||||
//Save the output
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public Hill() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
removePadding = false;
|
||||
setCharacterToAdd('x');
|
||||
reset();
|
||||
}
|
||||
public Hill(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, boolean removePadding) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
this.removePadding = removePadding;
|
||||
setCharacterToAdd('x');
|
||||
reset();
|
||||
}
|
||||
public Hill(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, boolean removePadding, char characterToAdd) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
this.removePadding = removePadding;
|
||||
setCharacterToAdd(characterToAdd);
|
||||
reset();
|
||||
}
|
||||
|
||||
public String encode(int[][] key, String inputString) throws InvalidKeyException, InvalidInputException{
|
||||
return encode(new ModMatrix(key, 26), inputString);
|
||||
}
|
||||
public String encode(ModMatrix key, String inputString) throws InvalidKeyException, InvalidInputException{
|
||||
setKey(key);
|
||||
setInputString(inputString);
|
||||
return encode();
|
||||
}
|
||||
public String decode(int[][] key, String inputString) throws InvalidKeyException, InvalidInputException{
|
||||
return decode(new ModMatrix(key, 26), inputString);
|
||||
}
|
||||
public String decode(ModMatrix key, String inputString) throws InvalidKeyException, InvalidInputException{
|
||||
setKey(key);
|
||||
setInputString(inputString);
|
||||
return decode();
|
||||
}
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
key = new ModMatrix(26);
|
||||
charsAdded = 0;
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public ModMatrix getKey(){
|
||||
return key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Morse.java
|
||||
//Matthew Ellison
|
||||
// Created: 07-28-21
|
||||
//Modified: 01-16-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
public class Morse{
|
||||
//Holds the Morse representation of the alphanumeric characters
|
||||
private static final String[] code = {
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", //A-L
|
||||
"--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", //M-Z
|
||||
"-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----." //0-9
|
||||
};
|
||||
private String inputString; //The string that needs encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
|
||||
//Encodes inputString and stores the result in outputString
|
||||
private String encode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
//Loop through every element in the input string and see what type it is
|
||||
for(char letter : inputString.toCharArray()){
|
||||
//If the character is a letter get teh correct combination from code and add it to the output
|
||||
if(Character.isUpperCase(letter)){
|
||||
output.append(code[letter - 65]);
|
||||
output.append(' ');
|
||||
}
|
||||
//If it is a number get the correct combination from code and add it to the output
|
||||
else if(Character.isDigit(letter)){
|
||||
int tempNum = Integer.parseInt(Character.toString(letter));
|
||||
output.append(code[tempNum + 26]);
|
||||
output.append(' ');
|
||||
}
|
||||
}
|
||||
//Remove the final space from the output
|
||||
if(output.length() > 0){
|
||||
output.replace(output.length() - 1, output.length(), "");
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString and stores the result in outputString
|
||||
private String decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(String current : inputString.split(" ")){
|
||||
boolean found = false;
|
||||
//Loop through the code and see if the current letter
|
||||
for(int cnt = 0;(cnt < 26) && (!found);++cnt){
|
||||
//See if current is the same as an element in code
|
||||
if(current.equals(code[cnt])){
|
||||
//Add 65 to cnt to get the correct capital letter
|
||||
output.append((char)(cnt + 'A'));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
//Loop through code and see if current is a number
|
||||
for(int cnt = 26;(cnt < 36) && (!found);++cnt){
|
||||
if(current.equals(code[cnt])){
|
||||
//Remove 26 from cnt to get the correct number
|
||||
output.append(Integer.toString(cnt - 26));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
//If it is neither print an error in the output
|
||||
if(!found){
|
||||
output.append("<Unknown symbol: " + current + ">");
|
||||
}
|
||||
}
|
||||
|
||||
//Save and return the output
|
||||
outputString = output.toString();
|
||||
return outputString;
|
||||
}
|
||||
//Encodes input and returns the result
|
||||
private void setEncodeInputString(String input){
|
||||
//Make sure the letters are all uppercase
|
||||
input = input.toUpperCase();
|
||||
//Remove all characters that are not capital letters
|
||||
input = input.replaceAll("[^A-Z0-9]", "");
|
||||
//Save the new input
|
||||
inputString = input;
|
||||
}
|
||||
//Decodes input and returns the result
|
||||
private void setDecodeInputString(String input){
|
||||
//Remove all characters except ., -, and ' '
|
||||
input = input.replaceAll("[^ \\.\\-]", "");
|
||||
//Save the new input
|
||||
inputString = input;
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Morse(){
|
||||
reset();
|
||||
}
|
||||
//Returns inputString
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
//Returns outputString
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
//Encodes input and returns the result
|
||||
public String encode(String input){
|
||||
setEncodeInputString(input);
|
||||
return encode();
|
||||
}
|
||||
//Decoes input and returns the result
|
||||
public String decode(String input){
|
||||
setDecodeInputString(input);
|
||||
return decode();
|
||||
}
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
inputString = outputString = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Playfair.java
|
||||
//Matthew Ellison
|
||||
// Created: 07-30-21
|
||||
//Modified: 02-17-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
|
||||
|
||||
public class Playfair{
|
||||
//A class representing the location of a character in the grid
|
||||
private class CharLocation{
|
||||
private int x;
|
||||
private int y;
|
||||
public CharLocation(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
public int getX(){
|
||||
return x;
|
||||
}
|
||||
public int getY(){
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
//Variables
|
||||
private boolean preserveCapitals; //Whether to respect captials in the output string
|
||||
private boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
private boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
private char replaced; //The letter that will need to be replaced in the grid and any input string or keyword
|
||||
private char replacer; //The letter that replaced replaced in the input string or keyword
|
||||
private char doubled; //The letter that will be placed between double letters in the input string if necessary or to make the string length even
|
||||
private String inputString; //The message that needs to be encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
private String keyword; //The keyword used to create the grid
|
||||
private char[][] grid; //The grid used to encode/decode the message
|
||||
//Create the grid from the keyword
|
||||
private void createGrid(){
|
||||
for(int row = 0;row < 5;++row){
|
||||
for(int col = 0;col < 5;++col){
|
||||
char letter = keyword.charAt((5 * row) + col);
|
||||
grid[row][col] = letter;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Strips invalid characters from the string that needs encoded/decoded
|
||||
private void setInputString(String inputString, boolean encoding) throws InvalidCharacterException, InvalidInputException{
|
||||
//Make sure the input string is not null
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("The input string cannot be null");
|
||||
}
|
||||
|
||||
//Set the options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Make replace all of the replacers with replaced
|
||||
inputString = inputString.replaceAll(Character.toString(replaced), Character.toString(replacer));
|
||||
|
||||
//If there is nothing in the input string throw an exception
|
||||
if(inputString.isBlank()){
|
||||
throw new InvalidCharacterException("The input string cannot be blank");
|
||||
}
|
||||
|
||||
//If this is encoding parse it and clean up an problems
|
||||
if(encoding){
|
||||
setEncodingInputString(inputString);
|
||||
}
|
||||
//If this is decoding just add it without parsing it
|
||||
else{
|
||||
//Throw an exception if replaced is included
|
||||
if(inputString.contains(Character.toString(replaced))){
|
||||
throw new InvalidCharacterException("An encoded message cannot contain a letter that needs replaced");
|
||||
}
|
||||
|
||||
this.inputString = inputString;
|
||||
}
|
||||
|
||||
if(this.inputString.isBlank() || getPreparedInputString().isBlank()){
|
||||
throw new InvalidInputException("Input must have at least 1 letter");
|
||||
}
|
||||
}
|
||||
private void setEncodingInputString(String inputString){
|
||||
//Replace characters that need replaced
|
||||
inputString = inputString.replaceAll(Character.toString(replaced), Character.toString(replacer));
|
||||
|
||||
//Check if there are any doubled characters
|
||||
StringBuilder cleanInput = new StringBuilder();
|
||||
int letterCount = 0;
|
||||
for(int cnt = 0;cnt < inputString.length();){
|
||||
//Advance until you find a letter, saving the input for inclusion
|
||||
StringBuilder prepend = new StringBuilder();
|
||||
while((cnt < inputString.length()) && !Character.isAlphabetic(inputString.charAt(cnt))){
|
||||
prepend.append(inputString.charAt(cnt++));
|
||||
}
|
||||
//If we have reached the end of the string end the loop
|
||||
if(cnt == inputString.length()){
|
||||
cleanInput.append(prepend);
|
||||
break;
|
||||
}
|
||||
//Get the next character
|
||||
char firstLetter = inputString.charAt(cnt++);
|
||||
++letterCount;
|
||||
|
||||
//Advance until you find a letter, saving the input for inclusion
|
||||
StringBuilder middle = new StringBuilder();
|
||||
while((cnt < inputString.length()) && !Character.isAlphabetic(inputString.charAt(cnt))){
|
||||
middle.append(inputString.charAt(cnt++));
|
||||
}
|
||||
char secondLetter = '\0';
|
||||
//If we have not reached the end of the string get the next character
|
||||
if(cnt != inputString.length()){
|
||||
secondLetter = inputString.charAt(cnt++);
|
||||
++letterCount;
|
||||
}
|
||||
//If the second character is the same as the first character set the pointer back and use the doubled character
|
||||
if(secondLetter == firstLetter){
|
||||
--cnt;
|
||||
secondLetter = doubled;
|
||||
}
|
||||
|
||||
//Add all of the gathered input to the cleaned up input
|
||||
cleanInput.append(prepend);
|
||||
cleanInput.append(firstLetter);
|
||||
cleanInput.append(middle);
|
||||
if(secondLetter != '\0'){
|
||||
cleanInput.append(secondLetter);
|
||||
}
|
||||
}
|
||||
|
||||
//Check if there are an odd number of characters
|
||||
if((letterCount % 2) == 1){
|
||||
int lastLetterLocation = cleanInput.length() - 1;
|
||||
while(!Character.isAlphabetic(cleanInput.charAt(lastLetterLocation))){
|
||||
--lastLetterLocation;
|
||||
}
|
||||
if(cleanInput.charAt(lastLetterLocation) == doubled){
|
||||
cleanInput.append(replacer);
|
||||
}
|
||||
else{
|
||||
cleanInput.append(doubled);
|
||||
}
|
||||
}
|
||||
|
||||
this.inputString = cleanInput.toString();
|
||||
}
|
||||
//Returns the input string ready for encoding
|
||||
private String getPreparedInputString(){
|
||||
String cleanString = inputString.toUpperCase();
|
||||
cleanString = cleanString.replaceAll("[^A-Z]", "");
|
||||
return cleanString;
|
||||
}
|
||||
//Strips invalid characters from the keyword and creates the grid
|
||||
private void setKeyword(String keyword){
|
||||
if(keyword == null){
|
||||
throw new NullPointerException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Change everything to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
|
||||
//Removing everything except capital letters
|
||||
keyword = keyword.replaceAll("[^A-Z]", "");
|
||||
|
||||
//Add all letters in the alphabet to the key
|
||||
keyword += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
//Replace all replaced characters
|
||||
keyword = keyword.replaceAll(Character.toString(replaced), Character.toString(replacer));
|
||||
|
||||
//Remove all duplicate chatacters
|
||||
StringBuilder uniqueKey = new StringBuilder();
|
||||
keyword.chars().distinct().forEach(c -> uniqueKey.append((char)c));
|
||||
this.keyword = uniqueKey.toString();
|
||||
|
||||
//Create the grid from the sanitized keyword
|
||||
createGrid();
|
||||
}
|
||||
//Returns the location of the given character in the grid
|
||||
private CharLocation findChar(char letter) throws InvalidInputException{
|
||||
for(int row = 0;row < grid.length;++row){
|
||||
for(int col = 0;col < grid[row].length;++col){
|
||||
if(grid[row][col] == letter){
|
||||
return new CharLocation(row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
//If it was not found something went wrong
|
||||
throw new InvalidInputException("The character '" + letter + "' was not found in the grid");
|
||||
}
|
||||
//Returns the location in the grid of x and y, adjusting for out of bounds
|
||||
private char getGridChar(int x, int y){
|
||||
if(x < 0){
|
||||
x += 5;
|
||||
}
|
||||
if(y < 0){
|
||||
y += 5;
|
||||
}
|
||||
return grid[x % 5][y % 5];
|
||||
}
|
||||
//Adds characters that aren't letters to the output
|
||||
private void addCharactersToCleanString(String cleanString){
|
||||
int outputCnt = 0;
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
if(Character.isUpperCase(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
}
|
||||
else if(Character.isLowerCase(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(Character.toLowerCase(cleanString.charAt(outputCnt++)));
|
||||
}
|
||||
else{
|
||||
fullOutput.append(inputString.charAt(inputCnt));
|
||||
}
|
||||
}
|
||||
outputString = fullOutput.toString();
|
||||
}
|
||||
//Encodes inputString using the Playfair cipher and stores the result in outputString
|
||||
private String encode() throws InvalidInputException{
|
||||
StringBuilder output = new StringBuilder();
|
||||
int inputCnt = 0;
|
||||
String cleanString = getPreparedInputString();
|
||||
while(inputCnt < cleanString.length()){
|
||||
//Get the next 2 letters to be encoded
|
||||
char firstLetter = cleanString.charAt(inputCnt++);
|
||||
char secondLetter = cleanString.charAt(inputCnt++);
|
||||
|
||||
//Find the letters in the grid
|
||||
CharLocation firstLocation = findChar(firstLetter);
|
||||
CharLocation secondLocation = findChar(secondLetter);
|
||||
|
||||
//Encode the letters
|
||||
if(firstLocation.getX() == secondLocation.getX()){
|
||||
firstLetter = getGridChar(firstLocation.getX(), firstLocation.getY() + 1);
|
||||
secondLetter = getGridChar(secondLocation.getX(), secondLocation.getY() + 1);
|
||||
}
|
||||
else if(firstLocation.getY() == secondLocation.getY()){
|
||||
firstLetter = getGridChar(firstLocation.getX() + 1, firstLocation.getY());
|
||||
secondLetter = getGridChar(secondLocation.getX() + 1, secondLocation.getY());
|
||||
}
|
||||
else{
|
||||
firstLetter = getGridChar(firstLocation.getX(), secondLocation.getY());
|
||||
secondLetter = getGridChar(secondLocation.getX(), firstLocation.getY());
|
||||
}
|
||||
|
||||
//Add the new letters to the output string
|
||||
output.append(firstLetter);
|
||||
output.append(secondLetter);
|
||||
}
|
||||
|
||||
//Add other characters to the output string
|
||||
addCharactersToCleanString(output.toString());
|
||||
|
||||
//Return the output string
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using the Playfair cipher and stores the result in outputString
|
||||
private String decode() throws InvalidInputException{
|
||||
StringBuilder output = new StringBuilder();
|
||||
int inputCnt = 0;
|
||||
String cleanString = getPreparedInputString();
|
||||
while(inputCnt < cleanString.length()){
|
||||
//Get the next 2 letters to be encoded
|
||||
char firstLetter = cleanString.charAt(inputCnt++);
|
||||
char secondLetter = cleanString.charAt(inputCnt++);
|
||||
|
||||
//Find the letters in the grid
|
||||
CharLocation firstLocation = findChar(firstLetter);
|
||||
CharLocation secondLocation = findChar(secondLetter);
|
||||
|
||||
//Decode the letters
|
||||
if(firstLocation.getX() == secondLocation.getX()){
|
||||
firstLetter = getGridChar(firstLocation.getX(), firstLocation.getY() - 1);
|
||||
secondLetter = getGridChar(secondLocation.getX(), secondLocation.getY() - 1);
|
||||
}
|
||||
else if(firstLocation.getY() == secondLocation.getY()){
|
||||
firstLetter = getGridChar(firstLocation.getX() - 1, firstLocation.getY());
|
||||
secondLetter = getGridChar(secondLocation.getX() - 1, secondLocation.getY());
|
||||
}
|
||||
else{
|
||||
firstLetter = getGridChar(firstLocation.getX(), secondLocation.getY());
|
||||
secondLetter = getGridChar(secondLocation.getX(), firstLocation.getY());
|
||||
}
|
||||
|
||||
//Add the new letters to the output string
|
||||
output.append(firstLetter);
|
||||
output.append(secondLetter);
|
||||
}
|
||||
|
||||
//Add other characters to the output string
|
||||
addCharactersToCleanString(output.toString());
|
||||
|
||||
//Return the output string
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public Playfair() throws InvalidCharacterException{
|
||||
reset();
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
setReplaced('J');
|
||||
setReplacer('I');
|
||||
setDoubled('X');
|
||||
}
|
||||
public Playfair(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
setReplaced('J');
|
||||
setReplacer('I');
|
||||
setDoubled('X');
|
||||
}
|
||||
public Playfair(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, char replaced, char replacer, char doubled) throws InvalidCharacterException{
|
||||
reset();
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
setReplaced(replaced);
|
||||
setReplacer(replacer);
|
||||
setDoubled(doubled);
|
||||
}
|
||||
//Sets the keyword and inputString and encodes the message
|
||||
public String encode(String keyword, String input) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(input, true);
|
||||
return encode();
|
||||
}
|
||||
//Sets the keyword and inputString and decodes the message
|
||||
public String decode(String keyword, String input) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputString(input, false);
|
||||
return decode();
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
grid = new char[5][5];
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
//Gets
|
||||
public char getReplaced(){
|
||||
return replaced;
|
||||
}
|
||||
public void setReplaced(char replaced) throws InvalidCharacterException{
|
||||
if(!Character.isAlphabetic(replaced)){
|
||||
throw new InvalidCharacterException("The replaced character must be a letter");
|
||||
}
|
||||
|
||||
if(replaced == replacer){
|
||||
throw new InvalidCharacterException("The replaced letter cannot be the same as the replacing letter");
|
||||
}
|
||||
|
||||
if(replaced == doubled){
|
||||
throw new InvalidCharacterException("The replaced letter cannot be the same as the replacing letter");
|
||||
}
|
||||
|
||||
this.replaced = Character.toUpperCase(replaced);
|
||||
}
|
||||
public char getReplacer(){
|
||||
return replacer;
|
||||
}
|
||||
public void setReplacer(char replacer) throws InvalidCharacterException{
|
||||
//Make sure the character is a letter
|
||||
if(!Character.isAlphabetic(replacer)){
|
||||
throw new InvalidCharacterException("The replacer must be a letter");
|
||||
|
||||
}
|
||||
|
||||
//Make sure the character isn't the same as what it is supposed to replace
|
||||
if(replacer == replaced){
|
||||
throw new InvalidCharacterException("The replacer cannot be the same letter as what it is replacing");
|
||||
}
|
||||
|
||||
//Make sure the replacer isn't the same as the double letter replacer
|
||||
if(replacer == doubled){
|
||||
throw new InvalidCharacterException("The replacer cannot be the same as the double letter replacer");
|
||||
}
|
||||
|
||||
this.replacer = Character.toUpperCase(replacer);
|
||||
}
|
||||
public char getDoubled(){
|
||||
return doubled;
|
||||
}
|
||||
public void setDoubled(char doubled) throws InvalidCharacterException{
|
||||
//Make sure the character is a letter
|
||||
if(!Character.isAlphabetic(doubled)){
|
||||
throw new InvalidCharacterException("The double letter replacement must be a letter");
|
||||
}
|
||||
|
||||
//Make sure the 2 replacers are the same
|
||||
if(doubled == replacer){
|
||||
throw new InvalidCharacterException("The double letter replacement cannot be the same as the regular replacer");
|
||||
}
|
||||
|
||||
//Make sure the double letter replacement isn't the same as the letter being replaced
|
||||
if(doubled == replaced){
|
||||
throw new InvalidCharacterException("The double letter replacement cannot be the same as the letter replaced");
|
||||
}
|
||||
|
||||
this.doubled = Character.toUpperCase(doubled);
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getGrid(){
|
||||
StringBuilder gridString = new StringBuilder();
|
||||
for(char[] row : grid){
|
||||
for(char col : row){
|
||||
gridString.append(col);
|
||||
}
|
||||
}
|
||||
|
||||
return gridString.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/PolybiusSquare.java
|
||||
//Mattrixwv
|
||||
// Created: 01-04-22
|
||||
//Modified: 02-17-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
|
||||
|
||||
public class PolybiusSquare{
|
||||
//A class representing the location of a character in the grid
|
||||
protected class CharLocation{
|
||||
private int x;
|
||||
private int y;
|
||||
public CharLocation(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
public int getX(){
|
||||
return x;
|
||||
}
|
||||
public int getY(){
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
//Variables
|
||||
protected String inputString; //The message that needs to be encoded/decoded
|
||||
protected String outputString; //The encoded/decoded message
|
||||
protected String keyword; //The keyword used to create the grid
|
||||
protected char[][] grid; //The grid used to encode/decode the message
|
||||
protected char replaced; //The letter that will need to be replaced in the grid and any input string or keyword
|
||||
protected char replacer; //The letter that replaces replaced in the input string or keyword
|
||||
protected boolean preserveWhitespace; //Whether to respect whitespace in the output string
|
||||
protected boolean preserveSymbols; //Whether to respect symbols in the output string
|
||||
|
||||
//Create the grid from the keyword
|
||||
protected void createGrid(){
|
||||
for(int row = 0;row < 5;++row){
|
||||
for(int col = 0;col < 5;++col){
|
||||
char letter = keyword.charAt((5 * row) + col);
|
||||
grid[row][col] = letter;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Strips invalid characters from the string that needs encoded/decoded
|
||||
protected void setInputStringEncoding(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Make sure the string doesn't contain any numbers
|
||||
for(char ch = '0';ch <= '9';++ch){
|
||||
if(inputString.contains(Character.toString(ch))){
|
||||
throw new InvalidCharacterException("Inputs for encoding cannot contain numbers");
|
||||
}
|
||||
}
|
||||
|
||||
//Change to upper case
|
||||
inputString = inputString.toUpperCase();
|
||||
|
||||
//Remove any whitespace if selected
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
|
||||
//Remove any symbols if selected
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
if(!preserveWhitespace && !preserveSymbols){
|
||||
//Add whitespace after every character for the default look
|
||||
StringJoiner spacedString = new StringJoiner(" ");
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
spacedString.add(Character.toString(inputString.charAt(cnt)));
|
||||
}
|
||||
inputString = spacedString.toString();
|
||||
}
|
||||
|
||||
//Replace any characters that need replaced
|
||||
inputString = inputString.replaceAll(Character.toString(replaced), Character.toString(replacer));
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank() || getPreparedInputStringEncoding().isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
protected void setInputStringDecoding(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
if(inputString == null){
|
||||
throw new NullPointerException("Input cannot be null");
|
||||
}
|
||||
//Make sure the string contains an even number of digits and no letters
|
||||
int numberOfDigits = 0;
|
||||
for(int cnt = 0;cnt < inputString.length();++cnt){
|
||||
char ch = inputString.charAt(cnt);
|
||||
if(Character.isDigit(ch)){
|
||||
++numberOfDigits;
|
||||
}
|
||||
else if(Character.isAlphabetic(ch)){
|
||||
throw new InvalidCharacterException("Inputs for decoding cannot contains letters");
|
||||
}
|
||||
}
|
||||
if((numberOfDigits % 2) != 0){
|
||||
throw new InvalidCharacterException("There must be an even number of digits in an encoded string");
|
||||
}
|
||||
|
||||
//Remove any whitespace if selected
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
|
||||
//Remove any symbols if selected
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^0-9\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
if(this.inputString.isBlank() || getPreparedInputStringDecoding().isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Returns the input string ready for encoding
|
||||
protected String getPreparedInputStringEncoding(){
|
||||
String cleanString = inputString.toUpperCase();
|
||||
cleanString = cleanString.replaceAll("[^A-Z]", "");
|
||||
return cleanString;
|
||||
}
|
||||
protected String getPreparedInputStringDecoding(){
|
||||
return inputString.replaceAll("[^0-9]", "");
|
||||
}
|
||||
//Strips invalid characters from the keyword and creates the grid
|
||||
protected void setKeyword(String keyword){
|
||||
if(keyword == null){
|
||||
throw new NullPointerException("Keyword cannot be null");
|
||||
}
|
||||
//Change everything to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
|
||||
//Remove everything except capital letters
|
||||
keyword = keyword.replaceAll("[^A-Z]", "");
|
||||
|
||||
//Add all letters in the alphabet to the key
|
||||
keyword += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
//Replace all replaced characters
|
||||
keyword = keyword.replaceAll(Character.toString(replaced), Character.toString(replacer));
|
||||
|
||||
//Remove all duplicate characters
|
||||
StringBuilder uniqueKey = new StringBuilder();
|
||||
keyword.chars().distinct().forEach(c -> uniqueKey.append((char)c));
|
||||
this.keyword = uniqueKey.toString();
|
||||
|
||||
//Create the grid from the sanitized keyword
|
||||
createGrid();
|
||||
}
|
||||
//Returns the location of the given charcter in the grid
|
||||
protected CharLocation findChar(char letter) throws InvalidInputException{
|
||||
for(int row = 0;row < grid.length;++row){
|
||||
for(int col = 0;col < grid[row].length;++col){
|
||||
if(grid[row][col] == letter){
|
||||
return new CharLocation(row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
//If it was not found something went wrong
|
||||
throw new InvalidInputException("The character '" + letter + "' was not found in the grid");
|
||||
}
|
||||
//Adds characters that aren't letters to the output
|
||||
protected void addCharactersToCleanStringEncode(String cleanString){
|
||||
int outputCnt = 0;
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
//Add both numbers of any letters to the output
|
||||
if(Character.isAlphabetic(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
}
|
||||
//Add any other characters that appear to the output
|
||||
else{
|
||||
fullOutput.append(inputString.charAt(inputCnt));
|
||||
}
|
||||
}
|
||||
outputString = fullOutput.toString();
|
||||
}
|
||||
protected void addCharactersToCleanStringDecode(String cleanString){
|
||||
int outputCnt = 0;
|
||||
StringBuilder fullOutput = new StringBuilder();
|
||||
for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){
|
||||
//Add the letter to the output and skip the second number
|
||||
if(Character.isDigit(inputString.charAt(inputCnt))){
|
||||
fullOutput.append(cleanString.charAt(outputCnt++));
|
||||
++inputCnt;
|
||||
}
|
||||
//Add any other characters that appear to the output
|
||||
else{
|
||||
fullOutput.append(inputString.charAt(inputCnt));
|
||||
}
|
||||
}
|
||||
outputString = fullOutput.toString();
|
||||
}
|
||||
//Encodes inputString using the Playfair cipher and stores the result in outputString
|
||||
private String encode() throws InvalidInputException{
|
||||
StringBuilder output = new StringBuilder();
|
||||
String cleanString = getPreparedInputStringEncoding();
|
||||
for(int cnt = 0;cnt < cleanString.length();++cnt){
|
||||
//Get the next character to be encoded
|
||||
char ch = cleanString.charAt(cnt);
|
||||
|
||||
//Find the letter in the grid
|
||||
CharLocation location = findChar(ch);
|
||||
|
||||
//Add the grid location to the output
|
||||
output.append(location.getX() + 1);
|
||||
output.append(location.getY() + 1);
|
||||
}
|
||||
|
||||
//Add other characters to the output string
|
||||
addCharactersToCleanStringEncode(output.toString());
|
||||
|
||||
//Return the output string
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using the Playfair cipher and stores the result in outputString
|
||||
private String decode(){
|
||||
StringBuilder output = new StringBuilder();
|
||||
String cleanString = getPreparedInputStringDecoding();
|
||||
for(int cnt = 0;cnt < cleanString.length();){
|
||||
//Get the digits indicationg the location of the next character
|
||||
char firstDigit = cleanString.charAt(cnt++);
|
||||
char secondDigit = cleanString.charAt(cnt++);
|
||||
|
||||
//Get the next character
|
||||
char letter = grid[Integer.valueOf(Character.toString(firstDigit)) - 1][Integer.valueOf(Character.toString(secondDigit)) - 1];
|
||||
|
||||
//Add the new letter to the output
|
||||
output.append(letter);
|
||||
}
|
||||
|
||||
//Add other characters to the output
|
||||
addCharactersToCleanStringDecode(output.toString());
|
||||
|
||||
//Return the output string
|
||||
return outputString;
|
||||
}
|
||||
|
||||
public PolybiusSquare() throws InvalidCharacterException{
|
||||
reset();
|
||||
setReplaced('J');
|
||||
setReplacer('I');
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
}
|
||||
public PolybiusSquare(boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
reset();
|
||||
setReplaced('J');
|
||||
setReplacer('I');
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
public PolybiusSquare(boolean preserveWhitespace, boolean preserveSymbols, char replaced, char replacer) throws InvalidCharacterException{
|
||||
reset();
|
||||
setReplaced(replaced);
|
||||
setReplacer(replacer);
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
//Sets the keyword and inputString and encodes the message
|
||||
public String encode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
return encode("", inputString);
|
||||
}
|
||||
public String encode(String keyword, String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputStringEncoding(inputString);
|
||||
return encode();
|
||||
}
|
||||
//Sets the keyword and inputString and decodes the message
|
||||
public String decode(String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
return decode("", inputString);
|
||||
}
|
||||
public String decode(String keyword, String inputString) throws InvalidCharacterException, InvalidInputException{
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setInputStringDecoding(inputString);
|
||||
return decode();
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
grid = new char[5][5];
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
}
|
||||
//Gets
|
||||
public char getReplaced(){
|
||||
return replaced;
|
||||
}
|
||||
public void setReplaced(char replaced) throws InvalidCharacterException{
|
||||
if(!Character.isAlphabetic(replaced)){
|
||||
throw new InvalidCharacterException("The replaced character must be a letter");
|
||||
}
|
||||
|
||||
if(replaced == replacer){
|
||||
throw new InvalidCharacterException("The replaced letter cannot be the same as the replacing letter");
|
||||
}
|
||||
|
||||
this.replaced = Character.toUpperCase(replaced);
|
||||
}
|
||||
public char getReplacer(){
|
||||
return replacer;
|
||||
}
|
||||
public void setReplacer(char replacer) throws InvalidCharacterException{
|
||||
if(!Character.isAlphabetic(replacer)){
|
||||
throw new InvalidCharacterException("The replacer character must be a letter");
|
||||
}
|
||||
|
||||
if(replaced == replacer){
|
||||
throw new InvalidCharacterException("The replacer letter cannot be the same as the replaced letter");
|
||||
}
|
||||
|
||||
this.replacer = replacer;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getGrid(){
|
||||
StringBuilder gridString = new StringBuilder();
|
||||
for(char[] row : grid){
|
||||
for(char col : row){
|
||||
gridString.append(col);
|
||||
}
|
||||
}
|
||||
|
||||
return gridString.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/RailFence.java
|
||||
//Mattrixwv
|
||||
// Created: 03-21-22
|
||||
//Modified: 03-22-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidBaseException;
|
||||
|
||||
|
||||
public class RailFence{
|
||||
//Variables
|
||||
private String inputString; //The message that needs to be encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
private StringBuilder[] fence; //The fence used for encoding/decoding
|
||||
private boolean preserveCapitals; //Persist capitals in the output string
|
||||
private boolean preserveWhitespace; //Persist whitespace in the output string
|
||||
private boolean preserveSymbols; //Persist symbols in the output string
|
||||
|
||||
//Strips invalid characters from the string that needs encoded/decoded
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
//Ensure the input string isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Ensures the number of rails is valid and sets up the fence
|
||||
private void setNumRails(int numRails) throws InvalidBaseException{
|
||||
if(numRails < 2){
|
||||
throw new InvalidBaseException("You must use at least 2 rails");
|
||||
}
|
||||
|
||||
fence = new StringBuilder[numRails];
|
||||
for(int cnt = 0;cnt < numRails;++cnt){
|
||||
fence[cnt] = new StringBuilder();
|
||||
}
|
||||
}
|
||||
//Strip the inputString of all non-letter characters
|
||||
private String getCleanInputString(){
|
||||
return inputString.replaceAll("[^a-zA-Z]", "");
|
||||
}
|
||||
//Ensures capitals, lowercase, and symbols are displayed in the output string
|
||||
private void formatOutput(String outputString){
|
||||
StringBuilder output = new StringBuilder();
|
||||
int outputLoc = 0;
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputLoc++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputLoc++)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
this.outputString = output.toString();
|
||||
}
|
||||
//Returns the decoded string found in the fence after all characters are placed correctly
|
||||
private String getDecodedStringFromFence(){
|
||||
boolean down = true;
|
||||
int rail = 0;
|
||||
int outsideCol = 0;
|
||||
int insideCol = -1;
|
||||
StringBuilder output = new StringBuilder();
|
||||
while(true){
|
||||
//Get the next character based on what rail you are currently usinig
|
||||
if(rail == 0){
|
||||
if(outsideCol >= fence[rail].length()){
|
||||
break;
|
||||
}
|
||||
output.append(fence[rail].charAt(outsideCol));
|
||||
++insideCol;
|
||||
}
|
||||
else if(rail == (fence.length - 1)){
|
||||
if(outsideCol >= fence[rail].length()){
|
||||
break;
|
||||
}
|
||||
output.append(fence[rail].charAt(outsideCol++));
|
||||
++insideCol;
|
||||
}
|
||||
else{
|
||||
if(insideCol >= fence[rail].length()){
|
||||
break;
|
||||
}
|
||||
output.append(fence[rail].charAt(insideCol));
|
||||
}
|
||||
|
||||
//Make sure you're still in bounds
|
||||
if(down){
|
||||
++rail;
|
||||
}
|
||||
else{
|
||||
--rail;
|
||||
}
|
||||
if(rail >= fence.length){
|
||||
down = false;
|
||||
rail -= 2;
|
||||
}
|
||||
else if(rail < 0){
|
||||
down = true;
|
||||
rail += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
//Encodes inputString using the RailFence cipher and stores the result in outputString
|
||||
private void encode(){
|
||||
boolean up = true;
|
||||
int rail = 0;
|
||||
for(char ch : getCleanInputString().toCharArray()){
|
||||
fence[rail].append(ch);
|
||||
//Advance to the next rail
|
||||
if(up){
|
||||
++rail;
|
||||
}
|
||||
else{
|
||||
--rail;
|
||||
}
|
||||
//Make sure you're still in bounds
|
||||
if(rail == fence.length){
|
||||
up = false;
|
||||
rail -= 2;
|
||||
}
|
||||
else if(rail == -1){
|
||||
up = true;
|
||||
rail += 2;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(StringBuilder segment : fence){
|
||||
output.append(segment);
|
||||
}
|
||||
|
||||
formatOutput(output.toString());
|
||||
}
|
||||
//Decodes inputString using the RailFence cipher and stores the result in outputString
|
||||
private void decode(){
|
||||
//Determine the number of characters on each rail
|
||||
String cleanInputString = getCleanInputString();
|
||||
int cycleLength = 2 * (fence.length - 1);
|
||||
BigDecimal k = new BigDecimal(cleanInputString.length()).divide(new BigDecimal(cycleLength));
|
||||
int numInTopRail = (int)Math.ceil(k.doubleValue());
|
||||
int numInMiddleRails = (numInTopRail * 2);
|
||||
int numInBottomRail = 0;
|
||||
boolean goingDown = true;
|
||||
int middleNum = 0;
|
||||
if(k.remainder(BigDecimal.ONE).compareTo(new BigDecimal("0.5")) <= 0){
|
||||
numInMiddleRails -= 1;
|
||||
numInBottomRail = (int)Math.floor(k.doubleValue());
|
||||
goingDown = true;
|
||||
middleNum = k.remainder(BigDecimal.ONE).multiply(new BigDecimal(cycleLength)).intValue() - 1;
|
||||
}
|
||||
else{
|
||||
numInBottomRail = numInTopRail;
|
||||
goingDown = false;
|
||||
middleNum = (cycleLength - (k.remainder(BigDecimal.ONE).multiply(new BigDecimal(cycleLength))).intValue());
|
||||
}
|
||||
|
||||
//Add the correct number of characters to each rail
|
||||
fence[0].append(cleanInputString.substring(0, numInTopRail));
|
||||
int start = numInTopRail;
|
||||
int end = numInTopRail + numInMiddleRails;
|
||||
for(int cnt = 1;cnt < (fence.length - 1);++cnt){
|
||||
if((!goingDown) && (middleNum >= cnt)){
|
||||
end -= 1;
|
||||
}
|
||||
fence[cnt].append(cleanInputString.substring(start, end));
|
||||
start = end;
|
||||
end += numInMiddleRails;
|
||||
}
|
||||
end = start + numInBottomRail;
|
||||
fence[fence.length - 1].append(cleanInputString.substring(start, end));
|
||||
|
||||
//Get the decoded string from the constructed fence
|
||||
String output = getDecodedStringFromFence();
|
||||
formatOutput(output);
|
||||
}
|
||||
|
||||
//Constructor
|
||||
public RailFence(){
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
}
|
||||
public RailFence(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
}
|
||||
|
||||
//Encodes inputString using a Rail Fence of length numRails and returns the result
|
||||
public String encode(int numRails, String inputString) throws InvalidBaseException, InvalidInputException{
|
||||
//Set the parameters
|
||||
setNumRails(numRails);
|
||||
setInputString(inputString);
|
||||
|
||||
//Encode
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using a Rail Fence of length numRails and returns the result
|
||||
public String decode(int numRails, String inputString) throws InvalidBaseException, InvalidInputException{
|
||||
//Set the parameters
|
||||
setNumRails(numRails);
|
||||
setInputString(inputString);
|
||||
|
||||
//Decode
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
fence = null;
|
||||
}
|
||||
//Gets
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public int getNumRails(){
|
||||
return fence.length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Trifid.java
|
||||
//Mattrixwv
|
||||
// Created: 03-03-22
|
||||
//Modified: 03-03-22
|
||||
package com.mattrixwv.cipherstream.polysubstitution;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
|
||||
import com.mattrixwv.cipherstream.exceptions.InvalidBaseException;
|
||||
|
||||
|
||||
public class Trifid{
|
||||
//A class representing the location of a character in the grid
|
||||
private class CharLocation{
|
||||
private int x;
|
||||
private int y;
|
||||
private int z;
|
||||
public CharLocation(int x, int y, int z){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
public int getX(){
|
||||
return x;
|
||||
}
|
||||
public int getY(){
|
||||
return y;
|
||||
}
|
||||
public int getZ(){
|
||||
return z;
|
||||
}
|
||||
public String toString(){
|
||||
return "[" + (z + 1) + ", " + (x + 1) + ", " + (y + 1) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
//Variables
|
||||
private String inputString; //The message that needs to be encoded/decoded
|
||||
private String outputString; //The encoded/decoded message
|
||||
private String keyword; //The keyword used to create the grid
|
||||
private int groupSize; //The size of the groups used to break up the input
|
||||
private char[][][] grid; //The grid used to encode/decode the message
|
||||
private char fillIn; //The character added to the alphabet to meet the 27 character requirement
|
||||
private boolean preserveCapitals; //Persist capitals in the output string
|
||||
private boolean preserveWhitespace; //Persist whitespace in the output string
|
||||
private boolean preserveSymbols; //Persist symbols in the output string
|
||||
|
||||
//Makes sure the fillIn is a valid character
|
||||
private void setFillIn(char fillIn) throws InvalidCharacterException{
|
||||
//Make sure the character is a printing character
|
||||
if((fillIn < ' ') || (fillIn > '~')){
|
||||
throw new InvalidCharacterException("Fill in character must be a printing character");
|
||||
}
|
||||
//Make sure the character is not a letter
|
||||
if(Character.isAlphabetic(fillIn)){
|
||||
throw new InvalidCharacterException("Fill in must not be a letter");
|
||||
}
|
||||
|
||||
//Save the fillIn character
|
||||
this.fillIn = fillIn;
|
||||
}
|
||||
//Strips invalid characters from the keyword and creates the grid
|
||||
private void setKeyword(String keyword) throws InvalidKeywordException{
|
||||
//Ensure the keyword isn't null
|
||||
if(keyword == null){
|
||||
throw new InvalidKeywordException("Keyword cannot be null");
|
||||
}
|
||||
|
||||
//Change everything to uppercase
|
||||
keyword = keyword.toUpperCase();
|
||||
|
||||
//Remove everything except capital letters
|
||||
keyword = keyword.replaceAll("[^A-Z" + fillIn + "]", "");
|
||||
|
||||
//Add all letters in the alphabet and fillIn to the key
|
||||
keyword += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + fillIn;
|
||||
|
||||
//Remove all duplicate characters
|
||||
StringBuilder uniqueKey = new StringBuilder();
|
||||
keyword.chars().distinct().forEach(c -> uniqueKey.append((char)c));
|
||||
this.keyword = uniqueKey.toString();
|
||||
|
||||
//Create the grid from the sanitized keyword
|
||||
createGrid();
|
||||
}
|
||||
//Creates the grid from the keyword
|
||||
private void createGrid(){
|
||||
for(int layerCnt = 0;layerCnt < grid.length;++layerCnt){
|
||||
for(int rowCnt = 0;rowCnt < grid[layerCnt].length;++rowCnt){
|
||||
for(int colCnt = 0;colCnt < grid[layerCnt][rowCnt].length;++colCnt){
|
||||
int loc = 0;
|
||||
loc = colCnt + (rowCnt * grid[layerCnt][rowCnt].length) + (layerCnt * grid[layerCnt].length * grid[layerCnt][rowCnt].length);
|
||||
grid[layerCnt][rowCnt][colCnt] = keyword.charAt(loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Ensures groupSize constraints
|
||||
private void setGroupSize(int groupSize) throws InvalidBaseException{
|
||||
if(groupSize <= 0){
|
||||
throw new InvalidBaseException("Group size must be > 0");
|
||||
}
|
||||
|
||||
this.groupSize = groupSize;
|
||||
}
|
||||
//Ensures inputString constraints
|
||||
private void setInputString(String inputString) throws InvalidInputException{
|
||||
//Ensure the input string isn't null
|
||||
if(inputString == null){
|
||||
throw new InvalidInputException("Input cannot be null");
|
||||
}
|
||||
|
||||
//Apply removal options
|
||||
if(!preserveCapitals){
|
||||
inputString = inputString.toUpperCase();
|
||||
}
|
||||
if(!preserveWhitespace){
|
||||
if(Character.isWhitespace(fillIn)){
|
||||
throw new InvalidInputException("If fillIn is whitespace, whitespace must be preserved");
|
||||
}
|
||||
inputString = inputString.replaceAll("\\s", "");
|
||||
}
|
||||
if(!preserveSymbols){
|
||||
inputString = inputString.replaceAll("[^a-zA-Z" + fillIn + "\\s]", "");
|
||||
}
|
||||
|
||||
//Save the string
|
||||
this.inputString = inputString;
|
||||
|
||||
//Ensure the string isn't blank
|
||||
if(this.inputString.isBlank()){
|
||||
throw new InvalidInputException("Input must contain at least 1 letter");
|
||||
}
|
||||
}
|
||||
//Returns the inputString with only letters
|
||||
private String getCleanInputString(){
|
||||
return inputString.toUpperCase().replaceAll("[^A-Z" + fillIn + "]", "");
|
||||
}
|
||||
//Returns the location of the given character in the grid
|
||||
private CharLocation findChar(char letter) throws InvalidCharacterException{
|
||||
for(int layer = 0;layer < grid.length;++layer){
|
||||
for(int row = 0;row < grid[layer].length;++row){
|
||||
for(int col = 0;col < grid[layer][row].length;++col){
|
||||
if(grid[layer][row][col] == letter){
|
||||
return new CharLocation(row, col, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If it was not found something went wrong
|
||||
throw new InvalidCharacterException("The character '" + letter + "' was not found in the grid");
|
||||
}
|
||||
//Return the character from the location provided
|
||||
private char getChar(CharLocation location) throws InvalidCharacterException{
|
||||
if(location.getX() > 2){
|
||||
throw new InvalidCharacterException("x cannot be larget than 2");
|
||||
}
|
||||
if(location.getY() > 2){
|
||||
throw new InvalidCharacterException("y cannot be larget than 2");
|
||||
}
|
||||
if(location.getZ() > 2){
|
||||
throw new InvalidCharacterException("z cannot be larget than 2");
|
||||
}
|
||||
|
||||
return grid[location.getZ()][location.getX()][location.getY()];
|
||||
}
|
||||
//Adds all non-letter characters back to the output string
|
||||
private void formatOutput(String outputString){
|
||||
//Keep track of where you are in the output
|
||||
int outputCnt = 0;
|
||||
StringBuilder output = new StringBuilder();
|
||||
//Check every character in the input and apply the correct rules to the output
|
||||
for(char ch : inputString.toCharArray()){
|
||||
if(Character.isUpperCase(ch)){
|
||||
output.append(Character.toUpperCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else if(Character.isLowerCase(ch)){
|
||||
output.append(Character.toLowerCase(outputString.charAt(outputCnt++)));
|
||||
}
|
||||
else{
|
||||
output.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
//Save the output
|
||||
this.outputString = output.toString();
|
||||
}
|
||||
//Encodes inputString using a polybius square and stores the result in outputString
|
||||
private void encode() throws InvalidCharacterException{
|
||||
//Step through every element in the sanitized inputString encoding the letters
|
||||
ArrayList<CharLocation> locations = new ArrayList<>();
|
||||
for(char ch : getCleanInputString().toCharArray()){
|
||||
//Get the location of the char in the grid
|
||||
CharLocation location = findChar(ch);
|
||||
locations.add(location);
|
||||
}
|
||||
|
||||
//Split the locations up by group
|
||||
int numGroups = inputString.length() / groupSize;
|
||||
if(numGroups == 0){
|
||||
numGroups = 1;
|
||||
}
|
||||
ArrayList<ArrayList<CharLocation>> groups = new ArrayList<>(numGroups);
|
||||
for(int cnt = 0;cnt < numGroups;++cnt){
|
||||
groups.add(new ArrayList<>());
|
||||
}
|
||||
int groupCnt = -1;
|
||||
for(int locCnt = 0;locCnt < locations.size();++locCnt){
|
||||
//If you've reached the end of the group move to the next one
|
||||
if((locCnt % groupSize) == 0){
|
||||
++groupCnt;
|
||||
}
|
||||
groups.get(groupCnt).add(locations.get(locCnt));
|
||||
}
|
||||
|
||||
//Split the coordinates into rows
|
||||
ArrayList<Integer> coordinates = new ArrayList<>(locations.size() * 3);
|
||||
for(ArrayList<CharLocation> group : groups){
|
||||
//Split the coordinates up into 3 rows
|
||||
ArrayList<Integer> layers = new ArrayList<>(group.size());
|
||||
ArrayList<Integer> rows = new ArrayList<>(group.size());
|
||||
ArrayList<Integer> cols = new ArrayList<>(group.size());
|
||||
for(CharLocation loc : group){
|
||||
layers.add(loc.getZ());
|
||||
rows.add(loc.getX());
|
||||
cols.add(loc.getY());
|
||||
}
|
||||
coordinates.addAll(layers);
|
||||
coordinates.addAll(rows);
|
||||
coordinates.addAll(cols);
|
||||
}
|
||||
//Create new locations from the rows of coordinates
|
||||
ArrayList<CharLocation> newLocations = new ArrayList<>(locations.size());
|
||||
for(int cnt = 0;cnt < coordinates.size();){
|
||||
int z = coordinates.get(cnt++);
|
||||
int x = coordinates.get(cnt++);
|
||||
int y = coordinates.get(cnt++);
|
||||
newLocations.add(new CharLocation(x, y, z));
|
||||
}
|
||||
|
||||
//Get the new letters from the grid
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(CharLocation loc : newLocations){
|
||||
output.append(getChar(loc));
|
||||
}
|
||||
|
||||
//Format the output
|
||||
formatOutput(output.toString());
|
||||
}
|
||||
//Decodes inputString using a polybius square and stores the result in outputString
|
||||
private void decode() throws InvalidCharacterException{
|
||||
//Step through every element in the sanitized inputString encoding the letters
|
||||
ArrayList<CharLocation> locations = new ArrayList<>();
|
||||
for(char ch : getCleanInputString().toCharArray()){
|
||||
//Get the location of the char in the grid
|
||||
CharLocation location = findChar(ch);
|
||||
locations.add(location);
|
||||
}
|
||||
|
||||
//Split the locations up by group
|
||||
int numGroups = inputString.length() / groupSize;
|
||||
if(numGroups == 0){
|
||||
numGroups = 1;
|
||||
}
|
||||
ArrayList<ArrayList<CharLocation>> groups = new ArrayList<>(numGroups);
|
||||
for(int cnt = 0;cnt < numGroups;++cnt){
|
||||
groups.add(new ArrayList<>());
|
||||
}
|
||||
int groupCnt = -1;
|
||||
for(int locCnt = 0;locCnt < locations.size();++locCnt){
|
||||
//If you've reached the end of the group move to the next one
|
||||
if((locCnt % groupSize) == 0){
|
||||
++groupCnt;
|
||||
}
|
||||
groups.get(groupCnt).add(locations.get(locCnt));
|
||||
}
|
||||
|
||||
//Split the coordinates into rows by group and create the original grid locations
|
||||
ArrayList<CharLocation> originalLocations = new ArrayList<>(locations.size());
|
||||
for(ArrayList<CharLocation> group : groups){
|
||||
//Read all of the coordinates from the group out into a row
|
||||
ArrayList<Integer> coordinates = new ArrayList<>(group.size() * 3);
|
||||
for(CharLocation loc : group){
|
||||
coordinates.add(loc.getZ());
|
||||
coordinates.add(loc.getX());
|
||||
coordinates.add(loc.getY());
|
||||
}
|
||||
|
||||
//Read out the coordinates into new locations
|
||||
ArrayList<CharLocation> originalGroup = new ArrayList<>(group.size());
|
||||
for(int cnt = 0;cnt < group.size();++cnt){
|
||||
originalGroup.add(new CharLocation(0, 0, 0));
|
||||
}
|
||||
int coordinateCnt = 0;
|
||||
for(CharLocation loc : originalGroup){
|
||||
loc.z = coordinates.get(coordinateCnt++);
|
||||
}
|
||||
for(CharLocation loc : originalGroup){
|
||||
loc.x = coordinates.get(coordinateCnt++);
|
||||
}
|
||||
for(CharLocation loc : originalGroup){
|
||||
loc.y = coordinates.get(coordinateCnt++);
|
||||
}
|
||||
originalLocations.addAll(originalGroup);
|
||||
}
|
||||
|
||||
//Get the original letters from the grid
|
||||
StringBuilder output = new StringBuilder();
|
||||
for(CharLocation loc : originalLocations){
|
||||
output.append(getChar(loc));
|
||||
}
|
||||
|
||||
//Format the output
|
||||
formatOutput(output.toString());
|
||||
}
|
||||
|
||||
|
||||
//Constructor
|
||||
public Trifid() throws InvalidCharacterException{
|
||||
preserveCapitals = false;
|
||||
preserveWhitespace = false;
|
||||
preserveSymbols = false;
|
||||
setFillIn('+');
|
||||
}
|
||||
public Trifid(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
setFillIn('+');
|
||||
}
|
||||
public Trifid(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols, char fillIn) throws InvalidCharacterException{
|
||||
this.preserveCapitals = preserveCapitals;
|
||||
this.preserveWhitespace = preserveWhitespace;
|
||||
this.preserveSymbols = preserveSymbols;
|
||||
setFillIn(fillIn);
|
||||
}
|
||||
|
||||
//Encodes inputString using keyword and groupSize and returns the result
|
||||
public String encode(String keyword, String inputString) throws InvalidBaseException, InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
return encode(keyword, inputString.length(), inputString);
|
||||
}
|
||||
public String encode(String keyword, int groupSize, String inputString) throws InvalidBaseException, InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setGroupSize(groupSize);
|
||||
setInputString(inputString);
|
||||
|
||||
//Encode and return the message
|
||||
encode();
|
||||
return outputString;
|
||||
}
|
||||
//Decodes inputString using keyword and groupSize and returns the result
|
||||
public String decode(String keyword, String inputString) throws InvalidBaseException, InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
return decode(keyword, inputString.length(), inputString);
|
||||
}
|
||||
public String decode(String keyword, int groupSize, String inputString) throws InvalidBaseException, InvalidKeywordException, InvalidInputException, InvalidCharacterException{
|
||||
//Set the parameters
|
||||
reset();
|
||||
setKeyword(keyword);
|
||||
setGroupSize(groupSize);
|
||||
setInputString(inputString);
|
||||
|
||||
//Decode and return the message
|
||||
decode();
|
||||
return outputString;
|
||||
}
|
||||
|
||||
//Makes sure all variables are empty
|
||||
public void reset(){
|
||||
inputString = "";
|
||||
outputString = "";
|
||||
keyword = "";
|
||||
groupSize = Integer.MAX_VALUE;
|
||||
grid = new char[3][3][3];
|
||||
}
|
||||
//Gets
|
||||
public String getInputString(){
|
||||
return inputString;
|
||||
}
|
||||
public String getOutputString(){
|
||||
return outputString;
|
||||
}
|
||||
public String getKeyword(){
|
||||
return keyword;
|
||||
}
|
||||
public int getGroupSize(){
|
||||
return groupSize;
|
||||
}
|
||||
public char getFillIn(){
|
||||
return fillIn;
|
||||
}
|
||||
public String getGrid(){
|
||||
StringJoiner layers = new StringJoiner("\n\n");
|
||||
|
||||
for(char[][] layer : grid){
|
||||
StringJoiner gridJoiner = new StringJoiner("\n");
|
||||
for(char[] row : layer){
|
||||
StringJoiner rowJoiner = new StringJoiner(" ", "[", "]");
|
||||
for(char ch : row){
|
||||
rowJoiner.add(Character.toString(ch));
|
||||
}
|
||||
gridJoiner.add(rowJoiner.toString());
|
||||
}
|
||||
layers.add(gridJoiner.toString());
|
||||
}
|
||||
|
||||
return layers.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user