diff --git a/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java b/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java index b96cafc..2f80e7f 100644 --- a/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java +++ b/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java @@ -1,13 +1,18 @@ //CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/polySubstitution/Hill.java //Mattrixwv // Created: 01-31-22 -//Modified: 02-11-22 +//Modified: 02-17-22 package com.mattrixwv.CipherStreamJava.polySubstitution; +import java.security.InvalidKeyException; import java.util.ArrayList; +import com.mattrixwv.CipherStreamJava.exceptions.InvalidCharacterException; +import com.mattrixwv.CipherStreamJava.exceptions.InvalidInputException; import com.mattrixwv.matrix.ModMatrix; +import com.mattrixwv.matrix.exceptions.InvalidGeometryException; +import com.mattrixwv.matrix.exceptions.InvalidScalarException; public class Hill{ @@ -21,78 +26,228 @@ public class Hill{ private int charsAdded; private ModMatrix key; - private void setKey(ModMatrix key){ - //TODO: + 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 = key.clone(); } - private void setInputString(String inputString){ - //TODO: + 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 void setCharacterToAdd(char characterToAdd){ - //TODO: - //!Don't forget to account for capitals + 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(); } - //TODO: private ArrayList getInputVectors(){ - //TODO: Return array of vectors //Get the number of columns in the key - return null; + int numCols = key.getNumCols(); + + //Get a clean inputString + String cleanInput = getCleanInputString(); + + //Break the inputString up into lengths of numCols + ArrayList 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 outputVectors){ - //TODO: Receive array of vectors - return null; + //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(){ - //TODO: - return null; + //Get an array of vectors that we are going to encode + ArrayList inputVectors = getInputVectors(); + + //Multiply the key by each vector and add the result to a new vector + ArrayList 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(){ - //TODO: - return null; + //Get the array of vectors that we are going to decode + ArrayList inputVectors = getInputVectors(); + + //Multiply the inverse of the key by each vector and add the result to a new vector + ModMatrix inverseKey = key.inverse(); + ArrayList 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(){ + 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){ + 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){ + 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){ + public String encode(int[][] key, String inputString) throws InvalidKeyException, InvalidInputException{ return encode(new ModMatrix(key, 26), inputString); } - public String encode(ModMatrix key, String inputString){ + public String encode(ModMatrix key, String inputString) throws InvalidKeyException, InvalidInputException{ setKey(key); setInputString(inputString); return encode(); } - public String decode(int[][] key, String inputString){ + public String decode(int[][] key, String inputString) throws InvalidKeyException, InvalidInputException{ return decode(new ModMatrix(key, 26), inputString); } - public String decode(ModMatrix key, String inputString){ + public String decode(ModMatrix key, String inputString) throws InvalidKeyException, InvalidInputException{ setKey(key); setInputString(inputString); return decode();