Files
CipherStreamJava/src/main/java/com/mattrixwv/cipherstream/monosubstitution/Autokey.java

210 lines
7.4 KiB
Java

//CipherStreamJava/src/main/java/com/mattrixwv/CipherStreamJava/Autokey.java
//Mattrixwv
// Created: 07-25-21
//Modified: 08-11-24
/*
Copyright (C) 2024 Mattrixwv
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.mattrixwv.cipherstream.monosubstitution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException;
/**
* Implements the Autokey cipher, an extension of the Vigenère cipher that uses a keyword combined with the plaintext itself as the key.
* The Autokey cipher adds the plaintext to the end of the keyword to create a longer key, which helps to make the encryption stronger.
* This class inherits from the {@code Vigenere} class and overrides methods to handle encoding and decoding with the Autokey cipher.
*
* <p>
* The Autokey cipher is symmetric, meaning that encoding and decoding are essentially the same process, but with a different key setup for decoding.
* </p>
*/
public class Autokey extends Vigenere{
private static final Logger logger = LoggerFactory.getLogger(Autokey.class);
/**
* Sets up the keyword and input string for encoding, generating a longer key that includes the plaintext.
* This method is used internally to prepare the cipher for encoding.
*
* @param keyword the keyword used for encoding
* @param inputString the string to be encoded
* @throws InvalidKeywordException if the keyword is invalid
* @throws InvalidInputException if the input string is invalid
*/
protected void encodeSet(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
logger.debug("Setting fields for encoding");
//Set the input
setInputString(inputString);
StringBuilder newKey = new StringBuilder();
//Remove all unneccessary elements from the key
logger.debug("Setting keyword");
setKeyword(keyword);
newKey.append(keyword);
//Remove all unneccessary elements from the input
logger.debug("Adding input to keyword");
setKeyword(inputString);
newKey.append(getKeyword());
//Make sure the key is not any longer than the input
logger.debug("Removing last letters in the keyword");
keyword = newKey.substring(0, getKeyword().length());
//Set the new keyword
setKeyword(keyword);
//Make sure to update the offset
offset.clear();
setOffset();
}
/**
* Sets up the keyword and input string for decoding. The keyword is used to decode the input string.
* This method is used internally to prepare the cipher for decoding.
*
* @param keyword the keyword used for decoding
* @param inputString the string to be decoded
* @throws InvalidKeywordException if the keyword is invalid
* @throws InvalidInputException if the input string is invalid
*/
protected void decodeSet(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
logger.debug("Setting fields for decoding");
//Remove all unneccessary elements from the key
logger.debug("Setting keyword");
setKeyword(keyword);
//Remove all unneccessary elements from the input
logger.debug("Setting input string");
setInputString(inputString);
}
/**
* Decodes the input string using the Autokey cipher.
* This method is overridden to handle decoding with the Autokey cipher, which involves using the key and updating it with decoded characters.
*/
@Override
protected void decode(){
logger.debug("Decoding");
//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()){
logger.debug("Appending partial output to keyword");
setKeyword(keyword + currentOutput.toString());
fullOutput.append(currentOutput);
currentOutput = new StringBuilder();
}
char letter = inputString.charAt(letterCnt);
logger.debug("Working character {}", letter);
if(Character.isUpperCase(letter)){
logger.debug("Appending uppercase");
letter -= offset.get((offsetCnt++) % offset.size());
if(letter < 'A'){
logger.debug("Wrapping around to Z");
letter += 26;
}
}
else if(Character.isLowerCase(letter)){
logger.debug("Appending lowercase");
letter -= offset.get((offsetCnt++) % offset.size());
if(letter < 'a'){
logger.debug("Wrapping around to z");
letter += 26;
}
}
logger.debug("Decoded letter {}", letter);
currentOutput.append(letter);
}
//Empty the last character that were decoded
fullOutput.append(currentOutput);
//Save and return the results
outputString = fullOutput.toString();
logger.debug("Saving output string '{}'", outputString);
}
//?Constructor
/**
* Constructs a new {@code Autokey} instance with default settings for preserving capitals, whitespace, and symbols.
*/
public Autokey(){
super();
}
/**
* Constructs a new {@code Autokey} instance with specified settings for preserving capitals, whitespace, and symbols.
*
* @param preserveCapitals whether to preserve capital letters in the output
* @param preserveWhitespace whether to preserve whitespace in the output
* @param preserveSymbols whether to preserve symbols in the output
*/
public Autokey(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
super(preserveCapitals, preserveWhitespace, preserveSymbols);
}
/**
* Encodes the input string using the Autokey cipher with the provided keyword.
*
* @param keyword the keyword used for encoding
* @param inputString the string to be encoded
* @return the encoded string
* @throws InvalidKeywordException if the keyword is invalid
* @throws InvalidInputException if the input string is invalid
*/
@Override
public String encode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
reset();
encodeSet(keyword, inputString);
encode();
return outputString;
}
/**
* Decodes the input string using the Autokey cipher with the provided keyword.
*
* @param keyword the keyword used for decoding
* @param inputString the string to be decoded
* @return the decoded string
* @throws InvalidKeywordException if the keyword is invalid
* @throws InvalidInputException if the input string is invalid
*/
@Override
public String decode(String keyword, String inputString) throws InvalidKeywordException, InvalidInputException{
reset();
decodeSet(keyword, inputString);
decode();
return outputString;
}
}