//Ciphers/Headers/Playfair.cpp //Matthew Ellison // Created: 4-25-18 //Modified: 5-16-18 //This file contains the implementation of the Playfair class //It is designed to encrypt and decrypt strings using the Playfair cipher #include "../Headers/Playfair.hpp" #include #include /** * @brief The current library's version number * */ const std::string Playfair::version = "1.0.1"; ///The letter that needs replaced for the cipher to work char Playfair::REPLACED = 'J'; ///The letter that replaces REPLACED char Playfair::REPLACER = 'I'; ///The letter that is added to the end to make the message of even length and placed between 2 ajoining letters that are the same char Playfair::DOUBLED = 'X'; /** * @brief Construct a new Playfair object * */ Playfair::Playfair(){ reset(); } /** * @brief Destroy the Playfair object * */ Playfair::~Playfair(){ } /** * @brief Uses keyword to set the characters in grid correctly * */ void Playfair::createGrid(){ unsigned int row, column; bool found = false; row = column = 0; //Add any new leters from the keyword to the grid ///If you reach row 5 then the entire grid has been filled char current; for(unsigned int cnt = 0;(cnt < keyword.size()) && (row < 5);++cnt){ current = keyword[cnt]; //If the current letter needs to be replaced, do so if(current == REPLACED){ current = REPLACER; } //Search the grid for the current letter found = checkGrid(current); //If the letter is not in the grid add it if(!found){ grid[row][column] = keyword[cnt]; ++column; //If the column number is too high reset it and advance the row if(column >= 5){ column = 0; ++row; } } } //Until you reach the end of the grid you need to start with the first letter and try to add them to the grid current = 'A'; while((row < 5) && (current <= 'Z')){ ////Start here //Make sure the is not the replaced letter if(current == REPLACED){ ++current; } //Check the grid for the current letter found = checkGrid(current); //If the letter was not found in the grid add it to the current location if(!found){ grid[row][column] = current; ++column; //If you have reached the end of the row move to the beginning of the next one if(column >= 5){ column = 0; ++row; } } ++current; } //Put a check here that row == 5. If it doesn't then there is a problem //TODO: Make this a propper exception throw rather than this if(row != 5){ //std::cout << "There is a problem with the grid!\n" << getGrid() << std::endl;; } } /** * @brief Checks if a letter is in the grid * * @param letter The letter you want to check for * @return true If the letter is found * @return false If the letter is not found */ bool Playfair::checkGrid(const char letter) const{ //Step through every element in the grid and check for it for(int rowCnt = 0;rowCnt < 5;++rowCnt){ for(int colCnt = 0;colCnt < 5;++colCnt){ //If you find the letter in the grid return true if(letter == grid[rowCnt][colCnt]){ return true; } //If you reach a \0 then there are no more symbols past that and no reason to continue checking else if(grid[rowCnt][colCnt] == '\0'){ return false; } } } //If it did not trigger the true, then it must be false return false; } /** * @brief Searches the grid for a letter * * @param letter The letter you wish to find * @param row The row that the letter is found in * @param col The column that the letter is found in */ void Playfair::searchGrid(char letter, int& row, int& col){ ////Start here //Check if letter needs to be replaced if(letter == REPLACED){ letter = REPLACER; } //Go through every element in the grid until you find the correct letter for(row = 0;row < 5;++row){ for(col = 0;col < 5;++col){ //If it is the correct letter you just need to return (row and col are passed by reference) if(grid[row][col] == letter){ return; } } } //If letter was not found you need to throw an error //TODO: Turn this into a propper exception throw rather than this //std::cout << "Error in searchGrid()\nLetter: " << letter << "\n\n" << getGrid() << std::endl; } /** * @brief Makes sure all variables are blank * */ void Playfair::reset(){ inputString = outputString = keyword = ""; //Clear the grid for(int cnt1 = 0;cnt1 < 5;++cnt1){ for(int cnt2 = 0;cnt2 < 5;++cnt2){ grid[cnt1][cnt2] = '\0'; } } } /** * @brief Encodes inputString using the rules of the Playfair cipher and stores it in outputString * * @return outputString */ std::string Playfair::encode(){ outputString = ""; char letter1, letter2; int row1, col1, row2, col2; //Step through every element in the input string and encode a pair for(unsigned int cnt = 1;cnt < inputString.size();cnt += 2){ //Grab two elements from the input string and search the grid for them letter1 = inputString[cnt - 1]; letter2 = inputString[cnt]; //Get the location in the grid of each letter searchGrid(letter1, row1, col1); searchGrid(letter2, row2, col2); //If the letters are in the same row, shift the column right by 1 if(row1 == row2){ ++col1; ++col2; //Wrap around if(col1 > 4){ col1 -= 5; } if(col2 > 4){ col2 -= 5; } } //If the letters are in the same column, shift the row down by 1 else if(col1 == col2){ ++row1; ++row2; //Wrap around if(row1 > 4){ row1 -= 5; } if(row2 > 4){ row2 -= 4; } } //If neither of the above then treat them like oposite corners in a square and swap their column variables else{ std::swap(col1, col2); } //Get the new letters letter1 = grid[row1][col1]; letter2 = grid[row2][col2]; //Add the new strings to the outputString outputString += letter1; outputString += letter2; } return outputString; } /** * @brief Uses keyword to set the grid, encodes input using the rules of the Playfair cipher, and returns the new message * * @param keyword The keyword used to create the grid * @param input The message that needs encoded * @return The encoded message */ std::string Playfair::encode(std::string keyword, std::string input){ setKeyword(keyword); setInputString(input); return encode(); } /** * @brief Decodes inputString using the rules of the Playfair cipher and stores it in outputString * * @return outputString */ std::string Playfair::decode(){ outputString = ""; char letter1, letter2; int row1, col1, row2, col2; //Step through every element in the input string and encode a pair for(unsigned int cnt = 1;cnt < inputString.size();cnt += 2){ //Grab two elements from the input string and search the grid for them letter1 = inputString[cnt - 1]; letter2 = inputString[cnt]; //Get the location in the grid of each letter searchGrid(letter1, row1, col1); searchGrid(letter2, row2, col2); //If the letters are in the same row, shift the column left by 1 if(row1 == row2){ --col1; --col2; //Wrap around if(col1 < 0){ col1 += 5; } if(col2 < 0){ col2 += 5; } } //If the letters are in the same column, shift the row up by 1 else if(col1 == col2){ --row1; --row2; //Wrap around if(row1 < 0){ row1 += 5; } if(row2 < 0){ row2 += 4; } } //If neither of the above then treat them like oposite corners in a square and swap their column variables else{ std::swap(col1, col2); } //Get the new letters letter1 = grid[row1][col1]; letter2 = grid[row2][col2]; //Add the new strings to the outputString outputString += letter1; outputString += letter2; } return outputString; } /** * @brief Uses keyword to set the grid, decodes input using the rules of the Playfair cipher, and returns the new message * * @param keyword The keyword used to create the grid * @param input The message that needs decoded * @return The decoded message */ std::string Playfair::decode(std::string keyword, std::string input){ setKeyword(keyword); setInputString(input); return decode(); } /** * @brief Removes all invalid characters and sets the keyword * * @param key The string that is used for the keyword */ void Playfair::setKeyword(std::string key){ //Make sure the keyword is blank keyword = ""; //Remove all punctuation and make everything capital for(unsigned int cnt = 0;cnt < key.size();++cnt){ //If it is an upper case letter, just add it to the keyword if(isupper(key[cnt])){ keyword += key[cnt]; } //If it is a lower case letter, make it upper case and add it to the keyword else if(islower(key[cnt])){ keyword += toupper(key[cnt]); } //If it is not a letter, then just ignore it } //Create the grid from the new keyword createGrid(); } /** * @brief Returns the current keyword * * @return The current keyword */ std::string Playfair::getKeyword() const{ return keyword; } /** * @brief Removes all invalid characters and sets inputString * * @param input The message that needs to be encoded */ void Playfair::setInputString(std::string input){ //Make sure inputString is empty inputString = ""; //Remove all punctuation and make everything capital for(unsigned int cnt = 0;cnt < input.size();++cnt){ char temp = input[cnt]; if(isupper(temp)){ //If the letter is the character than needs replaced, replace it if(temp == REPLACED){ temp = REPLACER; } inputString += temp; } else if(islower(temp)){ temp = toupper(temp); //If the letter is the character than needs replaced, replace it if(temp == REPLACED){ temp = REPLACER; } inputString += temp; } //If it is not a letter do nothing to it } //Check if anything is doubled and needs bumped down by DOUBLED for(unsigned int cnt = 1;cnt < inputString.size();){ char letter1, letter2; //Get two adjacent letters and compare them letter1 = inputString[cnt - 1]; letter2 = inputString[cnt]; //If they are the same insert DOUBLED between them if(letter1 == letter2){ inputString.insert((inputString.begin() + cnt), DOUBLED); } //If they are not the same, do nothing //Advance the counter by 2 cnt += 2; } //Check if the string length is odd and add DOUBLED to the end if it is if((inputString.size() % 2) == 1){ inputString += DOUBLED; } } /** * @brief Returns the current inputString * * @return The current message that needs encoded/decoded */ std::string Playfair::getInputString() const{ return inputString; } /** * @brief Returns the current outputString * * @return The encoded/decoded message */ std::string Playfair::getOutputString() const{ return outputString; } /** * @brief Returns a representation of the current grid * * @return A string representation of the grid */ std::string Playfair::getGrid() const{ std::string temp; for(int row = 0;row < 5;++row){ for(int col = 0;col < 5;++col){ temp += grid[row][col]; temp += " "; } temp += '\n'; } return temp; } /** * @brief Returns REPLACED * * @return The letter that needs replaced for the cipher to work */ char Playfair::getReplaced(){ return REPLACED; } /** * @brief Makes sure replaced is a valid character and sets REPLACED * * @param replaced The letter to replace REPLACED */ void Playfair::setReplaced(const char replaced){ //Make sure the letter is in the correct range if(replaced >= 'A' && replaced <= 'Z'){ //Make sure the letter is not the same as the one that is replacing it if(replaced != REPLACER){ REPLACED = replaced; } } else if(replaced >= 'a' && replaced <= 'z'){ REPLACED = toupper(replaced); } } /** * @brief Returned REPLACER * * @return The letter that replaces REPLACED */ char Playfair::getReplacer(){ return REPLACER; } /** * @brief Makes sure replacer is a valid character and sets REPLACER * * @param replacer The letter to replace REPLACER */ void Playfair::setReplacer(const char replacer){ //Make sure the letter is in the correct range if(replacer >= 'A' && replacer <='Z'){ //Make sure the letter is not the same as the one that it is replacing if(replacer != REPLACED){ REPLACER = replacer; } } else if(replacer >= 'a' && replacer <= 'z'){ REPLACER = toupper(replacer); } } /** * @brief Return DOUBLED * * @return The letter that is added to the end to make the message of even length and placed between 2 ajoining letters that are the same */ char Playfair::getDoubled(){ return DOUBLED; } /** * @brief Makes sure doubled is a valid character and sets DOUBLED * * @param doubled The letter to replace DOUBLED */ void Playfair::setDoubled(const char doubled){ //Make sure the letter is in the correct range if(doubled >= 'A' && doubled <= 'Z'){ DOUBLED = doubled; } else if(doubled >= 'a' && doubled <= 'z'){ DOUBLED = toupper(doubled); } } /** * @brief Returns a string containing the version information * * @return The version information */ std::string Playfair::getVersion(){ return version; }