//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 . */ 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. * *

* The Autokey cipher is symmetric, meaning that encoding and decoding are essentially the same process, but with a different key setup for decoding. *

*/ 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; } }